* Introduction Simple Emacs package to ease the use of build systems. By default it will use the directory ~build~ at the root of the project. It uses Emacs' built-in project.el. *THIS IS A WORK IN PROGRESS!* This package is not and should not be considered as stable. Expect unexpected changes at any time. ** Presentation Project compilation with Emacs is often tedious: there is no built-in system to easily run CMake, Meson or others with a single function, meaning you need to keep a terminal open to run the commands. *Builder* aims to solve this problem by automatically detecting available build systems for the project, and by proposing a command that will run in a compilation buffer. There is currently no way to have project local configuration, but this is a planned feature. * Usage By default it will use or create the directory ~build~ at the root of a project to generate the build system. You can change the name globally with the customizable variable ~builder-dir-name~. To run, just launch the function ~builder-configure~ or ~builder-compile~. There are demos in the ~builder-demo~ directory. ** Add a build system *** Detection You must first make the system detectable. To do this, you add an element to the alist ~builder--build-system-files~. The car of an element is the identifier of the system (a string, such as "make", "cargo", "cmake"...), and the cdr is the name of the characteristic file of the build system, located at the project's root. For "make" it is a file named "Makefile", for "cmake" it is "CMakeLists.txt" and so on. You can add an element to the list like this: #+BEGIN_SRC elisp (add-to-list builder-build-system-files '("make" "Makefile")) #+END_SRC *** Add the information Once the build system is detectable, you need to add the information about the instructions. It works like that: #+BEGIN_SRC elisp (builder-add-build-system :build-system-id "meson" :configure '(:instructions ((:name "release" :command "meson setup %b --buildtype=release") (:name "debug" :command "meson setup %b --buildtype=debug")) :function-modification builder--meson-configure-modify-command) :compile '(:instructions ((:name "default" :command "meson compile -C %b")))) #+END_SRC There are many things to note: - ~:build-system-id "meson"~: the build system identifier. Be careful to not mistype it and to check the case! - ~:configure~ and ~:compile~: currently there a two type of instructions. /configure/ and /compile/. They both take the same kind of arguments, a ~plist~ with those keys: + ~:instructions~: a list of /instructions/, a ~plist~ including a /command/, a name and other optional parameters - ~:name~: mandatory, the name of the instruction (a string) - ~:command~: mandatory, the command to run (a string). Before execution, it is formatted by ~builder-format-command~. For example, if the build directory name is "build" on a computer with 4 processor cores, "cmake --build %b --parallel %n" is transformed to "cmake --build \"build\" --parallel 4". See the doctring of ~builder-format-command~ for more information. - ~function--modification~: a function that take in argument a command (a string) and a directory. The returned value is the command to execute. Very useful if the command needs information only available at runtime. For example, to reconfigure /meson/ the flag ~--reconfigure~ needs to be added at the end of the command. ~:function-modification builder--meson-configure-modify-command~ automates this. ~function--modification~ is either instruction specific or global to the whole list of instructions (see below). + ~function--modification~: at this level, it affects all instructions of the instruction list. Same behaviour as described above. - ~:inside-directory t~ (not demonstrated here): whether the command needs to run inside or outside the build directory (a boolean). Like ~function--modification~, it can be instruction specific or not. False by default. ** Features *** Main features **** Build system detection Automatically detects your build system and only show relevant commands! **** Modification of the command on the fly Some build systems have specific needs. For instance, CMake's presets are stored into a JSon file. Builder automatically reads this file, and propose you available targets. Meson needs the flag ~--reconfigure~ if it was already configured. /Builder/ automatically appends it to the command when needed. ** All features *** Build directory - [X] Allow to change the default name - [ ] Multiple out-of-tree builds (removed because it was utterly broken) - [X] Possibility to have one build directory per branch - [X] Chose whether to run the command inside or outside the directory *** Detection - [X] Detect the build system - [X] Change the location of the search - [X] In case of multiple build systems, allow to select one - [ ] Override the detection locally *** Configure/compilation - [X] Chose between debug/release (or others) - [ ] *Easily* customize the command locally or globally - [X] Edit the command on the fly - [ ] Per project history - [X] Command formatting with format strings *** Program execution - [ ] Run the result of the compilation from Emacs * Design ** Definitions /Builder/ makes the artificial distinction between a command and an instruction: - Command : the plain text sent to the shell. For example, "make -j2" is a command. - Instruction : a structure containing a command and other information. For example, the following is an instruction: #+BEGIN_SRC elisp (:name "default" :command "cmake" :function-modification #'my-function) #+END_SRC ** Build system detection In the current implementation, build systems are detected by checking if specific files exist. For example, CMake is detected if a file ~CMakeLists.txt~ exists in the project directory. Then, the user is asked to select the build system from the list. It then returns a string, the identifier of the build system. You may notice that the detection is completely independent from the instructions. This is a deliberate choice, to allow better flexibility. For example, you could implement rules to only show a build system if some conditions are true; or to not display a build system if an alternative is also present. ** Build system table Information about build systems are stored in the variable ~builder--infos~. This is a hash table with build system ids as keys (e.g. "cmake", "meson"...). See the related doctring for more information. ** Instruction specific vs global Some settings can be set for a specific instruction, or for all instructions of the instruction list (globally). This is the case for ~function--modification~ for example. If it is set globally it can still be overridden on a per instruction basis. This is extremely useful if you want to exclude some instructions from the function without too much hassle. For example you can set this parameter to ~nil~ for a specific instruction, while all of the other while use your function. ** Project variables Currently there is no system to customize commands on a per project basis. The existing Emacs directory variables could be used, but there are some issues with that. The variables are actually *buffer* local, so changing the command would require to reload *every* open buffer, which would cause issues if some variables were modified. Also, the security system is great, but since commands cannot be safe, a warning would appear every time you open a buffer unless you set the values as safe (how much of a problem is that?). * Open questions - Should ~:function-modification~ be executed before ~builder-format-command~? After? Should ~builder-format-command~ not being called at all if ~function-modification~ is set? - Force build systems to use ~builder-dir-name~? Let them use their own defaults? For example OCaml uses ~_build~ by default. - Is the design sound enough? Could it theoretically support every past, present, and future build systems? - Is ~:function-modification~ well designed? - Are directory variable useful? Would project variable be sufficient? - More specifiers in ~builder-format-command~? - How to implement local variables? Leverage the existing infrastructure? - Should ~builder-compile~ and ~builder-configure~ be merged? Do they have virtually any difference? - Is a priority system useful (to rank available commands)? - Is the detection system able to handle hundreds of build systems? If it is not, what should be done? - ~builder-get-build-dir-name-with-build-configuration~ is kind of broken (and now removed), because the name of the configuration is only known at build time... Which means the function does not work correctly if it is not called from ~builder-compile~ or ~builder-configure~... It means that the configuration instruction and the compilation instruction need to have exactly the same name... Is there a better way to implement this functionality? * Troubleshooting ** My build system is not detected! Make sure you added a file to ~builder-build-system-files~ (and that there is not typo in the file name)! ** No commands are proposed! SO your build system is detected, but there is an error when you selected it. Did you use the function ~builder-add-build-system~? Are you sure you used the same identifier as for ~builder-build-system-files~? They both need to have the same case. ** Some configuration doesn't work! So some configurations such as ~:function-modification~ or ~:inside-directory~ have no effect? Be careful with the parenthesis: #+BEGIN_SRC elisp (builder-add-build-system :build-system-id "autotools" :configure '(:instructions ((:name "configure" :command "../configure") :inside-directory t))) #+END_SRC This example is incorrect: ~:inside-directory~ should be in the same context as ~:name~ (notice the parenthesis after ~:command "../configure"~). This is the correct version: #+BEGIN_SRC elisp (builder-add-build-system :build-system-id "autotools" :configure '(:instructions ((:name "configure" :command "../configure" :inside-directory t)))) #+END_SRC * Contributing I plan to propose this package to Emacs upstream, so if you want to contribute you have to sign the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Copyright-Assignment.html][copyright assignment]].