Skip to content

Latest commit

 

History

History
executable file
·
997 lines (871 loc) · 37.3 KB

emacsmake.org

File metadata and controls

executable file
·
997 lines (871 loc) · 37.3 KB

Emacs Make script

Introduction

This is a simple script to create Emacs from its source code. About the only thing missing from this is a licence, I haven’t come up with a usable licence yet.

Readme, in org format

* Introduction
This is a very simple wrapper script to distclean, configure, make, install and execute Emacs.
Changes can be made to the variables inside the script, which will help drive how Emacs can be
compiled, installed and executed.

* Variables and requirements
First off, you'll need to install the source code to Emacs, adjust =EMACSCOMPILEHOME=. You can either
fetch and extract a static tarball of the source, or you can access either of the git repositories.

Tarballs are usually a fixed point in the stage of Emacs development, but git repositories have
multiple threads of development, meaning there's a requirement to select the correct one if you
don't simply want to use the latest available code, often labeled the master branch. This is always
a moving target, so people often select a version branch, or perhaps a specific point in one of
those branches, such as an official release point.

Tarballs are created at these points, making them useful for snapshots where nothing much will
change in the future. It also means there's no expected future additions to that development unless
you make the effort to update on a regular basis.

If you grab code from the git repositories, be aware that there are multiple branches of Emacs code
that you can build, so check out what's available for compiling, select one, and you'll be off. You
will of course need to know how to use git, and how to select the correct branch of Emacs to
compile.

** Compile requirements
Because you're compiling Emacs, you can choose which libraries to compile Emacs with; adjust the
=EMACSCONFIGPARAMS= array for this. You might want to check the output of =emacsmake -ch= from
within the source directory to see what parameters are supported.

This will include choosing the place to put the compiled version of Emacs, adjust =EMACSHOME=. Note
that this is the root of the tree where the whole of Emacs will eventually live, not the location of
the Emacs binary itself. Usually directories will be created below this point for the libraries, the
binaries and the compiled lisp code to live. The usual place that is selected if you don't specify
it would be =/usr/local=, just as with a lot of other configure-based build systems.

If you're also going to compile with support for other things such as libjansson, then make sure the 
required -dev/-devel packages are also installed.

* Executing Emacs
At the moment, this script will run Emacs using the =with-emacs.sh= script, available from
https://github.com/alphapapa/with-emacs.sh - if you wish to run the compiled Emacs from anywhere not
already on the user's =PATH=, you will want to either add the installation directory (of the Emacs
binary): 
#+BEGIN_QUOTE bash 
$ export PATH=${PATH}:${HOME}/bin/<new-location>/bin/emacs 
#+END_QUOTE
as an example, or edit =with-emacs.sh= suitably to run the Emacs binary from a new location. Don't
forget to install the script somewhere on your =PATH=.

To supply a different configuration directory to execute Emacs with, adjust EMACSCONFHOME. This will
affect the running of with-emacs.sh.

* Future directions
At this stage, I haven't got the foggiest idea what else I need, though I've already simplified this
by removing the whole "execute Emacs" stage, leaving that up to the with-emacs.sh script.

The usual applies to anyone who wants to report bugs, issues or even suggestions with emacsmake, check out the
issues section of this project.

If you want to report a bug regarding Emacs itself, they have a mailing list hosted at =https://lists.gnu.org/emacs-devel/= and the project page is hosted at =http://savannah.gnu.org/projects/emacs/=

Main file

Header and Changelog

A few notes made about things added/changed, along with a long-term target to meet for Emacsen versions earlier than 28.0.50. Heck, 18.x (and earlier beasts) were done on far wimpier machines and earlier versions (13-18.58) predated the autoconf software suite which wasn’t released until 1991.

#!/bin/bash
#
# v0.1 Initial cut, should be mostly complete.
# v0.2 Changes to account for mistakes I made.
# v0.3 rework of environment variables.
# v0.4 added distclean, maintainerclean, and run everything.
# v0.5 Renamed reference from emacs-sandbox to with-emacs as upstream has renamed.
# v0.6 Finally added link to with-emacs.sh - it's about time.
#      Added comment for checking number of args at runtime.
# v0.7 Added in a do-almost-all (clean, configure, make).
# v0.8 Added some commented out options (lucid and motif).
# v0.8.1 Twiddled with config, error's only in the docs.
# v0.8.2 fixed docs for config with ugly hack.
# v0.9 Planning install upgrade to symlink the Emacs binary to emacs-${VER}-$[WMGR}
# v0.10 Reworked all the pushd/popd to be quiet, by redirecting their output to /dev/null.
# v0.11 Created new param to combine make and install, deliberately doesn't include config.
# v0.12 Begin process of pulling in vars from environment if already provided.
# v0.13 Now we've got the main x-toolkit targets, how about --without-x?
# v0.14 We're adding makeAllToolkits, and makeMeBootstrap.
# v0.15 Corrected bug in -mat where gtk3 asked for but gtk2 compiled.
# v0.16 Added an unPatch switch (-pu) to reverse patches already applied with -p.
# v0.17 Some more tweaks, adding --without-native-compilation re: emacs-devel posts.
# v0.18 Corrected emacs to Emacs where it makes sense to.
# v0.19 Added -ch, to call configure --help. I also made some whitespace changes.
#
# TODO: rework for versions of Emacs earlier than 28.0.50, as there's no makefile until the
#       configure phase. Still doesn't work for much earlier than 23 - 18.59 has no configure.

VC ChangeLog

Parameters

So far, the only thing of note here is that we need a specific directory to be explicitly mentioned so that calling this from sudo doesn’t bork with messages such as:

/home/viking/bin/emacsmake: line 157: pushd: /root/src/c/emacs: No such file or directory

Anyhow, there are some other tweakable values here which make sense except perhaps the playpen, which ends up being my own personal settings testbed, so I don’t blow away my main configuration.

Another thought—we could perhaps pull in our EMACSCONFIGPARAMS from a created-at-first-run file. The concept would be:

  • If we don’t have a defaults file already
    • prompt user for “Choose, or defaults” (*)
    • write file back (for next time)
  • otherwise, read that file, and prompt user
    • Defaults are ….., do you wish to change these?
    • if so, select other defaults
    • write file back (for next time)

(*) We need to tweak EMACSCOMPILEHOME, EMACSHOME and EMACSCONFIGPARAMS. The other two (EMACSCONFHOME and EMACSRUNARGS) aren’t needed for compiling, merely for running.

We could do it nice and simply, and just write the config code as bash, so that simply sourcing it would do the job nicely. However, I’m not sure how that works for overriding defaults in the current script regarding arrays. I’ve no reason to believe it won’t work, but I don’t know as much as I’d like to.

Currently variables just get set to whatever made sense in the script, but there’s no reason why we can’t support env vars from the outside either.

I deliberately haven’t selected aot, as the extra files takes up some space on drive.

#######################
# Modifiable parameters
# You WILL want to fiddle with these if you don't want the args I chose
# This WILL break if the user running (EUID) isn't the same as the user owning the source directory
# i.e. if there's no /root/src/c/emacs
#EMACSCOMPILEHOME="${HOME}/src/c/emacs" # Should be specific, not user-relative

# Home for config file, if there's one. Let's make it somewhat XDG-compliant.
EMACSMAKECONF="${XDG_CONFIG_HOME}/emacsmake_conf.sh"

# Provide for the option to feed these vars in from the outside
# and allows us to pull in from somewhere else
EMACSCOMPILEHOME=${EMACSCOMPILEHOME:-"/h3/viking/src/c/hosts/gnu/emacs"}
# Place to put Emacs (root of tree)
# EMACSHOME="${HOME}/bin/emacs-playpen"
EMACSHOME=${EMACSHOME:-"/usr/local"}
# Default, but check the configMe functions further down. Can't feed this in from user's env.
# Emacs-23 doesn't have most of these options
# This is default for the moment, switch if needed via command line switches
EMACSNATIVE="--without-native-compilation"
EMACSCONFIGPARAMS=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-xwidgets"
    "--with-x-toolkit=gtk3" # for completeness's sake
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
# Default X toolkit if we don't change it with a -c{g2,l,m,n} option
WMGR=gtk3
# This gets used to run with-emacs.sh with custom directory
EMACSCONFHOME="${HOME}/.emacs-playpen"
EMACSRUNARGS=(
    "-d"
    "${EMACSCONFHOME}"
)

Functions

Help function

The boilerplate help just in case I forgot what switches I coded. Uses UNIX syntax (-x) and not Microsoft (/x). There’s also a slight bug with EMACSCONFIGPARAMS if I decide I want to show non-default window toolkits. So far I’ve added GTK, GTK2, GTK3 (the default), Lucid and Motif toolkits. It is very difficult if not impossible to compile Emacs so that it supports multiple toolkits.

One other issue is that I’ve defaulted to having treesitter and ImageMagick, which isn’t necessarily true for most other machines this might be executed on. There will eventually be a way to set up what libs you want, but that’ll rely on a whole lot of booleans up near the top of the file which can be read in from disk, or put up near the top of the emacsmake script instead.

###########
# Functions

# Help function, usage(), ugly hack to put in toolkits
EMACSCONFIGPARAMSGTK=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-x-toolkit=gtk" # for completeness's sake
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
EMACSCONFIGPARAMSGTK2=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-x-toolkit=gtk2" # for completeness's sake
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
EMACSCONFIGPARAMSGTK3=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-xwidgets"
    "--with-x-toolkit=gtk3" # for completeness's sake
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
#EMACSCONFIGPARAMSATHENA=(
#    "--with-modules"
#    "--with-tree-sitter"
#    "--with-imagemagick"
#    "--with-cairo"
#    "--with-x-toolkit=athena"
#    "--prefix=${EMACSHOME}"
#    "${EMACSNATIVE}"
#)
EMACSCONFIGPARAMSLUCID=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-x-toolkit=lucid"
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
EMACSCONFIGPARAMSMOTIF=(
    "--with-modules"
    "--with-tree-sitter"
    "--with-imagemagick"
    "--with-cairo"
    "--with-x-toolkit=motif"
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)
EMACSCONFIGPARAMSNOX=( # You know I just had to do it
    "--with-modules"
    "--with-tree-sitter"
    "--without-x"
    "--prefix=${EMACSHOME}"
    "${EMACSNATIVE}"
)

helpMe() {
    echo "$0: Emacs recompiler script"
    echo "  -h    help (this text)"
    echo "  -d    Runs 'make distclean'"
    echo "  -D    Runs 'make maintainer-clean'"
    echo "  -g    Runs a 'git pull' from the top of the tree"
    echo "  -p    patch sourcetree from patch files in patches/ directory"
    echo "        This takes NO account of already-patched files"
    echo "  -pu   Undoes patches previously applied with -p in reverse order"
    echo "        Again, this takes NO account of already unpatched files"
    echo "        Errors from these two switches will be from patch, not $0"
    echo "  -a    Runs configure, make and install - will make gtk3 client"
    echo "  -e    run every step; distclean, configure, make, install, run"
    echo "        default Emacs binary location is ${EMACSHOME}"
    echo "  -c    run ./configure with params ${EMACSCONFIGPARAMS[@]}"
    echo "  -ch  run ./configure to extract all its help"
    echo "  -cg   run ./configure with params ${EMACSCONFIGPARAMSGTK[@]}"
    echo "  -cg2  run ./configure with params ${EMACSCONFIGPARAMSGTK2[@]}"
#    echo "  -ca   run ./configure with params ${EMACSCONFIGPARAMSATHENA[@]}"
    echo "  -cl   run ./configure with params ${EMACSCONFIGPARAMSLUCID[@]}"
    echo "  -cm   run ./configure with params ${EMACSCONFIGPARAMSMOTIF[@]}"
    echo "  -cn   run ./configure with params ${EMACSCONFIGPARAMSNOX[@]}"
    echo "  -mb   compile (no install), runs make with bootstrap"
    echo "  -m    compile (no install), runs make"
    echo "  -i    install to ${EMACSHOME}, runs make install"
    echo "  -mi   combines compile and install steps"
    echo "  -mat  configure, make, install all toolkits"
    echo "        You should NOT be running Emacs when you use this"
    echo "  -r    execute from ${EMACSHOME}, runs  with-emacs.sh -d "${EMACSCONFHOME}" -i quelpa-use-package "
    echo "  -u   uninstall from ${EMACSHOME}, runs make uninstall"
    echo "       This will uninstall the most recently installed Emacs if you"
    echo "       built from this exact commit, otherwise behaviour is undefined"
}

Update source tree with git

I had seriously considered this, but I can’t even guarantee I’ll stick just with emacs-29, and I don’t really know how to propagate that through the build scripts that depend on it, so I’m shelving the idea for now at least.

I would ordinarily have run emacsmake -d initially, followed by a call out to git pull, and then the remainder of this script can just continue on. One other thing is that if I wanted to apply patches before I started configuring, my script would also have to handle that too. What a headache!

Well, I guess I can put git into practice, though I’m not sure exactly how to retain branch yet. Patching (of course) will generally be up to the user anyhow. This will just run “git pull”. We should run make distclean first before we git pull. Unfortunately, this doesn’t handle patched-already files.

# This can generally update the git tree in the manner prescribed by the user.
gitMe() {
    make distclean # This just makes sure that we have no stray files left lying around
    git pull # This doesn't take account of patch files already applied.
}

Patching

I have a very small patch in place at the moment for upyoursgrandma.patch, the other patch I’ll leave out for the moment.

# This can generally patch the source tree in the manner prescribed by the user.
# Leave suitable patches in the patches/ directory.
# This does not take account of already-patched files. Beware. May error
# if there are no patch files.
patchMe() {
    for t in patches/*.patch; do
       patch -p1<${t}
    done
}

# This is meant to apply the patches in reverse order. Errors if there are none.
unPatchMe() {
    local demFiles=(patches/*.patch)
#    for t in patches/*.patch; do
     for ((t=${#demFiles[@]}-1; t>=0; t--)); do
        patch -Rp1<${demFiles[$t]}
    done
}

Cleaning scripts distclean/maintainer-clean

Distclean basically removes almost everything that didn’t come with the source code, and maintainer-clean removes everything that was generated, leaving only the original code. I’m not sure about the distinction, though I used to know. One point I’ve noted is that neither command is gatekeepered, i.e. they both take immediate effect without the ability to back out.

# Runs make distclean, but only if the configure step had created one.
# TODO: no chance to break out of this, perhaps we should offer that
cleanMe() {
    if [[ -f Makefile ]]; then
	echo "This will REMOVE all compiled files including makefiles"
	make distclean
    else
	echo "Makefile not found, skipping"
    fi
}

# Same proviso as above, this command takes immediate effect
cleanMeGood() {
    if [[ -f Makefile ]]; then
	echo "This will REMOVE all compiled files including makefiles"
	make maintainer-clean
    else
	echo "Makefile not found, skipping"
    fi
}

Configure Emacs for compilation

It’s pretty obvious what this does, though it does presume we’re in the correct directory, which we should be by the time we get here. Heck, if we weren’t, something went badly wrong.

I learned to specify the location of the configure directly, so I don’t accidentally pick up on a preinstalled configure somewhere in the ${PATH}.

I’m also adding a shortcut to the –help from configure, though you really could get this for yourself by running it by hand. It’s still up to you to pipe it to a pager, especially if you’re on a 24/25-line screen.

# This extracts the help from ./configure --help
configHelpMe() {
 if [[ -f ./configure ]]; then
        ./configure --help
    else
        ./autogen.sh # fails if autotools not installed
        if [[ ! -f configure ]]; then
            echo "Failed to create configure, do you have autotools installed?"
        else
            ./configure --help
        fi
    fi
}

# Runs configure phase, runs autogen if configure isn't here already
# This just does GTK3 unless EMACSCONFIGPARAMS[] is changed from a calling function
configMe() {
    if [[ -f ./configure ]]; then
        ./configure "${EMACSCONFIGPARAMS[@]}"
    else
        ./autogen.sh # fails if autotools not installed
        if [[ ! -f configure ]]; then
            echo "Failed to create configure, do you have autotools installed?"
        else
            ./configure "${EMACSCONFIGPARAMS[@]}"
        fi
    fi
}

# Should support the GTK1 crowd but here, just grabs gtk3
configMeGtk() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--with-imagemagick"
        "--with-cairo"
        "--with-x-toolkit=gtk"
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=gtk # (might be gtk3)
    configMe
}

# Should support the GTK2 crowd too
configMeGtk2() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--with-imagemagick"
        "--with-cairo"
        "--with-x-toolkit=gtk2"
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=gtk2
    configMe
}


# Default unless something else chosen, simply call configMe
configMeGtk3() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--with-imagemagick"
        "--with-cairo"
        "--with-xwidgets"
        "--with-x-toolkit=gtk3" # for completeness's sake
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=gtk3
    configMe
}

# Choose this if you never installed gtk - crazy, huh?
configMeLucid() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--with-imagemagick"
        "--with-cairo"
        "--with-x-toolkit=lucid"
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=lucid
    configMe
}

# Choose this if you have athena and maybe not lucid
# commented out while I check feasibility.
#configMeAthena() {
#    EMACSCONFIGPARAMS=(
#        "--with-modules"
#        "--with-tree-sitter"
#        "--with-imagemagick"
#        "--with-cairo"
#        "--with-x-toolkit=athena"
#        "--prefix=${EMACSHOME}"
#        "${EMACSNATIVE}"
#    )
#    WMGR=athena
#    configMe
#}

# Choose this if you actually have a Motif or LessTif
configMeMotif() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--with-imagemagick"
        "--with-cairo"
        "--with-x-toolkit=motif"
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=motif
    configMe
}

# Choose this if you don't want an Emacs with X (creates emacs-nox)
configMeNoX() {
    EMACSCONFIGPARAMS=(
        "--with-modules"
        "--with-tree-sitter"
        "--without-x"
        "--prefix=${EMACSHOME}"
        "${EMACSNATIVE}"
    )
    WMGR=nox
    configMe
}

Make commands

The only note here is that we force a full recompile so we don’t accidentally install stale .elc files from previous compiles.

# Runs make (hopefully we ran configure first)
makeMeBootstrap() {
    if [[ -f Makefile ]]; then
	make bootstrap -j4 # because we want stale .elc files regenned
    else
	echo "No Makefile found, perhaps run with -c/-cn/-cl/-cm/-cg2/-cg3 first?"
    fi
}

makeMe() {
    if [[ -f Makefile ]]; then
	make -j4 # because we don't want .elc files regenned
    else
	echo "No Makefile found, perhaps run with -c/-cn/-cl/-cm/-cg2/-cg3 first?"
    fi
}

This function assumes that because we want “everything”, that is, all the toolkits, it also assumes that we’ll have run distclean, git, patch before entering this.

This should do the following:

  • nox. (-cn, -m, -i); deliberately runs bootstrap for the first batch
  • motif (-cm, make -j4, -i); from here, don’t run bootstrap (speeds things up)
  • athena (-ca, make -j4, -i) (not yet running)
  • lucid (-cl, make -j4, -i)
  • gtk2 (-cg2, make -j4, -i)
  • gtk3 (-c, make -j4, -i); we install this last so it ends up being the default
# Yes, we want to make and install all the toolkits
makeAllToolkits() {
    # First, the -nox (the next ones don't really matter what order)
    configMeNoX
    makeMeBootstrap  # because we want to pick up the "make bootstrap"
    installMe
    # Then motif, no need to rebuild .elc, but *.eln could be a problem
    configMeMotif
    makeMe
    installMe
    # athena - commented out until we confirm it works.
#    configMeAthena
#    makeMe
#    installMe
    # lucid
    configMeLucid
    makeMe
    installMe
    # gtk2
    configMeGtk2
    makeMe
    installMe
    # and last, the default of gtk3
    configMeGtk3
    makeMe
    installMe
}

This function’s meant to find our currently-running Emacs and check if it’s in a place that will be replaced by installing. If so, we should warn the user, especially if we’re compiling from within Emacs itself (i.e. a vterm).

A point was raised that this might not work if PREFIX/POSTFIX were used to transform the binary during installation. I’ll have to perhaps look at that later, as I need to find out whether that affects things for this script in particular.

The question about what to do hasn’t been addressed yet; do we:

warn the user and continue on
This won’t be useful if the user has kicked it off and switched away. We can’t do much about this, it’s on them. It’ll simply complain it can’t replace the running binary.
warn the user, and wait for a short interval
Again, if user switches away on the premise that this switch is for unattended use (a reasonable expectation) they won’t be happy. Again, it’s on them. Then of course we perhaps need to provide a way to exit out.
warn user and block
This is the most intrusive. In the event that the user wants to switch

away, they’ll find that the warning has blocked them from continuing the compile until they either hit “Yes”, “Space” or some other key showing assent.

warn user and exit with error code
This means they won’t get a compiled Emacs of any sort if they’re running it, so they can’t simply sit around in #emacs while they wait for the compile;

it’ll never start.

These options all affect the installation of the newly-compiled Emacs, hence attempting to replace the running binary. It shouldn’t theoretically affect the compile itself.

Technically, wouldn’t this affect the -install target too?

# We need a "check we're not running Emacs" function
checkRunningEmacs() {
    # First let's check that Emacs is NOT running. This way we can replace it.
    rtval=$(pidof emacs)
    # this line will NOT work if we have multiple PIDs, we then need to check multiple locations
    RUNNINGEXE="$(basename $(readlink /proc/${rtval}/exe) )"
    # Grab the location we're about to overwrite
    EMACSTARGET="$(basename $(readlink ${EMACSHOME}/bin/emacs) )"

    # If this location would get overwritten (as with /usr/local/emacs)
    if [[ ${EMACSTARGET} == ${RUNNINGEXE} ]]; then
        # bleat
        echo "This switch will want to replace the Emacs you're currently running"
    fi
}

Install/uninstall

This is a way to find out what I already compiled, if I compiled it. We start off with a default of gtk3 in WMGR, which only changes if we find something specific. We don’t really have to cut off the pattern itself and it would save us a step through cut.

If we compiled without a toolkit altogether, this will have to be added to, as there’d be no other switches to look for. There must be some way of getting a non-X Emacs compiled too.

# Find what I made already, this is normally called from installMe
whatMadeMe() {
    # Piece to load into an array so I can parse the --with-x-toolkit parameter
    # VERY sensitive to configure output - if it changes, then this won't work.
    # If I search for $ ./configure I'll find the right line.
    if [[ -f config.log ]]; then
        mapfile -t TOOLKIT < <(sed -n '/\$ .\/configure/p' config.log | cut -c17-)
        for this in ${TOOLKIT[@]}; do
            case "${this}" in
                "--with-x-toolkit=gtk") WMGR=gtk ;; # Works out to be gtk3 here
                "--with-x-toolkit=gtk2") WMGR=gtk2 ;;
                "--with-x-toolkit=gtk3") WMGR=gtk3 ;;
#                "--with-x-toolkit=athena") WMGR=athena ;;
                "--with-x-toolkit=lucid") WMGR=lucid ;;
                "--with-x-toolkit=motif") WMGR=motif ;;
                "--without-x") WMGR=nox ;;
            esac # We can simply ignore everything else
        done # for this in ${TOOLKIT}
        printf "Toolkit is: %s\n" ${WMGR}
    else
        echo "I don't think you ran configure in ${EMACSCOMPILEHOME}. Please rerun emacsmake with a -c parameter"
        popd >/dev/null # just to keep the stack straight
        exit 1 # Shouldn't really exit here
    fi
}

This piece copies the generated Emacs binary at the destination to a backup that has the X toolkit name appended.

# This copies the delivered binary ${EMACSHOME}/bin/emacs${VERSION} to emacs-${VERSION}-$[WMGR}
# which will be something like emacs-29.1.50-{gtk2,gtk3,lucid,motif}
# May not work for versions earlier than 28
copyMe() {
    # If we fail to find an Emacs here, we failed to install
    if [[ -f ${EMACSHOME}/bin/emacs ]]; then
        EMACSTGT="$(readlink ${EMACSHOME}/bin/emacs)"
        echo "Linking ${EMACSHOME}/bin/${EMACSTGT} to ${EMACSHOME}/bin/${EMACSTGT}-${WMGR}"
        # There should perhaps be a sudo/doas here
        sudo cp -v ${EMACSHOME}/bin/${EMACSTGT} ${EMACSHOME}/bin/${EMACSTGT}-${WMGR}
    else
        echo "$0: we couldn't find an Emacs at ${EMACSHOME} or ${WMGR} isn't set."
    fi
}

This should by rights require the user to use sudo for this phase, but I have no clue as to what’s on a target machine for sudo permissions, if the user even has sudo rights. Otherwise, it’s a stock standard install-emacs-to-target-directory at ${EMACSHOME} specified earlier in the PARAMS section. One point not in the defaults is to copy the binary to a file that has the toolkit embedded in the name.

# Runs the install phase (currently don't need sudo, but would have normally done)
installMe() {
    # echo "This will require you to enter in your password" # only needed for system dirs
    # sudo make install
    # TODO: should check that there's an Emacs binary first, but I don't know where that will be
    # First, find out what we made from what's been left behind in the last compile
    whatMadeMe
    if [[ -z ${WMGR} ]]; then
        echo "No toolkit selected (not even -nox), you should probably have run emacsmake -c"
    fi
    # There should perhaps be a sudo/doas here
    sudo make install
    copyMe
}

# Uninstall from $EMACSHOME
uninstallMe() {
    # The only requirements are that I've installed Emacs at EMACSHOME
    # and NOT reconfigured Emacs since
    if [[ -f "${EMACSHOME}/bin/emacs" ]]; then
        make uninstall
    else
        echo "Are you sure you installed Emacs to ${EMACSHOME}?"
    fi
}

Running

This runs the just-installed Emacs from a config in sandbox, so I don’t hose the main config. It does depend on an ancillary script called with-emacs.sh, which came from another place. It’s not mandatory to have this script for anything other than running Emacs with the config. I just hope I made a note of where to get it from, if I don’t have it here on the local machine.

# Assuming everything else is done, runs compiled Emacs from install
runMe() {
    pushd "${EMACSHOME}"
    RETVAL=$( with-emacs.sh "${EMACSRUNARGS[@]}" )
    if [[ "${RETVAL}" != 0 ]]; then
        echo "Completed with ${RETVAL}"
    else
        echo "Completed with success"
    fi
    popd >/dev/null
}

Do-everything section

To call this, we just string all the sections together in a sequence. I’ve made no attempt to support multiple stages, and have explicitly disallowed this by only allowing one arg down further in main().

# Do almost everything
execMakeMe() {
    cleanMe # it's a distclean
    configMe
    makeMeBootstrap # bootstrap just for good measure
}

# Do everything except runMe - may require sudo
execMe() {
    execMakeMe # distclean, config and make
    installMe # Don't add copyMe to this, it's called from here already
#    runMe
}

Main

This handles the arg matching, checks if we have a with-emacs.sh, and notifies us if we don’t. It’s assumed we know how to get this from Github if we don’t have it, perhaps an incorrect assumption. It’s available from Github at the following link (assuming he didn’t remove it): https://github.com/alphapapa/with-emacs.sh

We explicitly disallow multiple stages from being run (except the ones I’ve specifically allowed), as we need to process things in a certain order and it’s too complex for me to track what stages I’ve set, especially if I try doing install folllowed by uninstall, we’d have to reorder this so the uninstall was done first even if we specified it last.

I’ve also not made any attempt at using getopts, as I feel it’s simply quicker to roll my own. However, I could have used short/long switches like the following:

The -e:/–emacs: switch was the precursor for the “run Emacs from this location instead” function from with-emacs.sh, but as the script does stuff better than I would have done, I left that up to the secondary script. It’s not written by me, and the style is quite different - presumably someone who knows what they’re doing. The -e switch has been reutilised for “–everything”, but might end up being removed along with the -a switch while I consider what best to use.

########
# main()

# First, let user know about with-emacs.sh if they don't already have it installed
SANDBOX_LOCATION=$(type -p with-emacs.sh)
if [[ -z "${SANDBOX_LOCATION}" ]]; then
    echo "You do not have with-emacs.sh (check README.org), you should probably grab this"
    echo "from the following URL: https://github.com/alphapapa/with-emacs.sh"
    echo "so you can run Emacs from a sandboxed location."
    echo "Continuing anyhow."
fi
unset SANDBOX_LOCATION

# TODO: need a better switch parser.
if [[ -n $2 ]]; then # In short, if 2 or more args
    echo "$0: Too many arguments, we only need one of the following"
    helpMe
elif [[ -n $1 ]]; then
    case $1 in 
        "-h"|"--help"|"-?") helpMe ;;
        "-ch"|"--confighelp")  pushd "${EMACSCOMPILEHOME}" >/dev/null
              configHelpMe
              popd >/dev/null ;;
        "-d") pushd "${EMACSCOMPILEHOME}" >/dev/null
              cleanMe
              popd >/dev/null ;;
	"-D") pushd "${EMACSCOMPILEHOME}" >/dev/null
              cleanMeGood
              popd >/dev/null ;;
        "-g") pushd "${EMACSCOMPILEHOME}" >/dev/null
              gitMe
              popd >/dev/null ;;
        "-p") pushd "${EMACSCOMPILEHOME}" >/dev/null
              patchMe
              popd >/dev/null ;;
        "-pu") pushd "${EMACSCOMPILEHOME}" >/dev/null
              unPatchMe
              popd >/dev/null ;;
        "-a") pushd "${EMACSCOMPILEHOME}" >/dev/null
              execMakeMe
              popd >/dev/null ;;
        "-e") pushd "${EMACSCOMPILEHOME}" >/dev/null # Eventually changes to ${EMACSHOME}
              execMe
              popd >/dev/null ;;
        "-c") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMe
              popd >/dev/null ;; # Uses GTK3 with xwidgets
        "-cg") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMeGtk
              popd >/dev/null ;;
        "-cg2") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMeGtk2
              popd >/dev/null ;;
        "-cl") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMeLucid
              popd >/dev/null ;;
        "-cm") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMeMotif
              popd >/dev/null ;;
        "-cn") pushd "${EMACSCOMPILEHOME}" >/dev/null
              configMeNoX
              popd >/dev/null ;;
        "-mb")pushd "${EMACSCOMPILEHOME}" >/dev/null
              makeMeBootstrap
              popd >/dev/null ;;
        "-m") pushd "${EMACSCOMPILEHOME}" >/dev/null
              makeMe # without bootstrap
              popd >/dev/null ;;
        "-i") pushd "${EMACSCOMPILEHOME}" >/dev/null
              installMe
              popd >/dev/null ;;
        # Let's do this all here
        "-mi") pushd "${EMACSCOMPILEHOME}">/dev/null
              makeMeBootstrap
              installMe
              popd >/dev/null ;;
        # Gotta catch them all! Check the function comment.
        "-mat")  pushd "${EMACSCOMPILEHOME}">/dev/null
              makeAllToolkits
              popd >/dev/null ;;
        "-r") pushd "${EMACSHOME}">/dev/null
              runMe
              popd >/dev/null ;;
	"-u") pushd "${EMACSCOMPILEHOME}" >/dev/null
              uninstallMe
              popd >/dev/null ;;
        # Find out what we have before
        "-w") pushd "${EMACSCOMPILEHOME}" >/dev/null
              whatMadeMe
              popd >/dev/null ;;
        *) pushd "."
              helpMe ;;
    esac
else # We don't have $1
    helpMe
fi

echo "Ending... bye"

Makefile

This is a dirt-simple Makefile that will hopefully install emacsmake into a system-wide location. It will need several runs at this, and I need to remember that recipes start off with a flush-left line, followed by tabbed recipe instructions.

I deliberately haven’t used “install” as I don’t know what it does. I’ve also embedded a sudo inside the makefile, which probably isn’t a great idea either, but hey, it’s my own machine. This is also not for use on a Windows or MS-DOS/FreeDOS install.

I also haven’t created a TAGS/tags file from here, as the local tools here are a bit non-standard.

# Dirt-simple Makefile to install the emacsmake script
# v0.1 Initial cut - it only has to install one thing.

DESTDIR = /usr/local/bin

install: emacsmake
	@sudo cp -v emacsmake ${DESTDIR}/
	@sudo chmod -v +x ${DESTDIR}/emacsmake

uninstall:
	@sudo rm -v ${DESTDIR}/emacsmake

Conclusions

This was originally a fairly clean script, which would have been easy enough to understand without the extra descriptions. There’s not a lot to this one, but I felt I needed to add a little clarification at certain spots. I’ve also tried to make it more “portable” between Emacs versions.

There’s also no config file, and one could be used to help clean the script up. Reasonable defaults would still be needed.

Bits to add

I have to sew in some infrastructure for toggling native compilation without going the route of environment variables. I don’t have it at the moment, and the way I set up main() assumes that I’m only using one switch and everything else is signalled as an error.

Nothing else to add yet.