fmake

make any project just by typing `fmake`
git clone git@getsh.org:fmake.git
Log | Files | Refs | README | LICENSE

commit fdaf28d9eec999deef4b0ae67ed6b0106a26041e
parent 64a1c9cb7d8bbc3934115dc13c72e45afa85a2e8
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date:   Thu,  4 Jul 2024 04:10:15 +0530

Add support for selecting build systems with exts

Remove enum and use macros for general ease of use.

Diffstat:
MLICENSE | 0
MMakefile | 9++++++---
MREADME | 12+++++++++---
Mconfig.h | 71+++++++++++++++++++++++++++++++----------------------------------------
Mfmake.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Atodo.txt | 21+++++++++++++++++++++
6 files changed, 154 insertions(+), 62 deletions(-)

diff --git a/LICENSE b/LICENSE diff --git a/Makefile b/Makefile @@ -1,14 +1,17 @@ -include config.mk +VERSION = 0.1.3 + +CFLAGS += -DFMAKE_VERSION=$(VERSION) -Wall -Wextra -g +PREFIX ?= /usr/local all: fmake -fmake: fmake.c +fmake: fmake.c config.h clean: rm -f fmake install: fmake - mkdir -p $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f fmake $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/fmake diff --git a/README b/README @@ -14,9 +14,12 @@ Opinionates build directory as 'out' in case of no clear build 'out' directory s USAGE ----- -To avoid complexity, fmake does not fork any process to run the commands. It just prints out the right command. -`alias make='$(fmake)'` should be added in your .bash_profile, or .zshrc for fmake to work. +Just type fmake, in your project directory. + + alias m=fmake -a + +to quickly run fmake commands. When none is found, it just defaults to the `make` in $PATH @@ -28,4 +31,7 @@ Supported build files * configure * gradle * ninja -* Much more to be added soon +* nobuild, +* and much more! + +Find the complete list in config.h diff --git a/config.h b/config.h @@ -1,47 +1,38 @@ -#include <stdint.h> - -typedef enum { - FMAKE_POSIX_MAKEFILE, - FMAKE_GNU_MAKEFILE, - FMAKE_BSD_MAKEFILE, - FMAKE_NINJA, - FMAKE_O_MAKEFILE, - FMAKE_CONFIGURE, - FMAKE_AUTORECONF, - FMAKE_CMAKE, - FMAKE_GN, - FMAKE_NPM, - FMAKE_RUST, - FMAKE_PIP, - FMAKE_GRADLE, -} maker_t; +#define BUILD_SYSTEMS \ + X(0, "Makefile" , "make") \ + X(0, "makefile" , "make") \ + X(0, "GNUMakefile" , "gmake") \ + X(0, "BSDMakefile" , "bmake") \ + X(1, "pro" , "qmake") \ + X(0, "make" , "sh" , "make") \ + X(0, "build.sh" , "sh" , "build.sh") \ + X(0, "build.ninja" , "ninja") \ + X(0, "OMakefile" , "omake") \ + X(0, "configure" , "sh" , "configure") \ + X(0, "configure.ac" , "autoreconf" , "-fiv") \ + X(0, "CMakeLists.txt" , "cmake" , "-B" , "out/") \ + X(0, "BUILD.gn" , "gn" , "gen" , "out/") \ + X(0, "nob" , "./nob" , "./nob") \ + X(0, "nob.c" , "cc" , "cc" , "./nob.c" , "-o" , "nob") \ + X(0, "nobuild" , "./nobuild" , ) \ + X(0, "nobuild.c" , "cc" , "./nobuild.c" , "-o" , "nobuild") \ + X(0, "package.json" , "npm" , "run") \ + X(0, "Cargo.toml" , "cargo" , "build") \ + X(0, "setup.py" , "pip" , "install" , ".") \ + X(0, "gradlew.bat" , "./gradlew.bat" , "./gradlew.bat") \ + X(0, "gradlew" , "sh" , "gradlew") \ + X(0, "PKGBUILD" , "makepkg" , "-i") typedef struct { - const char* filename; - maker_t type; - const char* cmd; - const char* args; +short check_extension; +const char* filename; +const char* cmd; +const char* args[256]; } maker_config_t; static const maker_config_t makers[] = { -{ "Makefile", FMAKE_POSIX_MAKEFILE, "make", "" }, -{ "makefile", FMAKE_POSIX_MAKEFILE, "make", "" }, -{ "GNUMakefile", FMAKE_GNU_MAKEFILE, "gmake", "" }, -{ "BSDMakefile", FMAKE_BSD_MAKEFILE, "bmake", "" }, -{ "build.ninja", FMAKE_NINJA, "ninja", "" }, -{ "OMakefile", FMAKE_O_MAKEFILE, "omake", "" }, -{ "configure", FMAKE_CONFIGURE, "sh", "configure" }, -{ "configure.ac", FMAKE_AUTORECONF, "autoreconf", "-i" }, -{ "CMakeLists.txt", FMAKE_CMAKE, "cmake", "-B out" }, -{ "BUILD.gn", FMAKE_GN, "gn", "gen out" }, -{ "package.json", FMAKE_NPM, "npm", "install" }, -{ "Cargo.toml", FMAKE_RUST, "cargo", "install" }, -{ "setup.py", FMAKE_PIP, "pip", "install ." }, -#ifdef _WIN32 -{ "gradlew.bat", FMAKE_GRADLE, "./gradlew.bat", "" }, -#endif -{ "gradlew", FMAKE_GRADLE, "sh", "gradlew" }, +#define X(ISEXT, LOOKFOR, CMD, ...) {ISEXT, LOOKFOR, CMD, { CMD, __VA_ARGS__} }, +BUILD_SYSTEMS +#undef X }; -static maker_config_t maker; - diff --git a/fmake.c b/fmake.c @@ -1,30 +1,101 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> #include "config.h" -void process_build() { - printf("%s %s", maker.cmd, maker.args); -} +static maker_config_t maker; +static DIR *dir; +static struct dirent *entry; +static short should_execute_commands = 0; +static short is_accepting_cmd_args = 0; + + +#define info(...) \ + if (should_execute_commands) { \ + fprintf(stderr, __VA_ARGS__); \ + } -struct stat st = {0}; -void process_string() { - for (int i = 0; i < (sizeof(makers) / sizeof(maker_config_t)); i++) { - const char* filename = makers[i].filename; - if (!stat(filename, &st)) { - maker = makers[i]; - process_build(); - return; +int process_build(char* argv[]) { + int status = -1; + info("++"); + for(size_t i = 0; i < sizeof(maker.args) && maker.args[i] != NULL; i++) { + fprintf(should_execute_commands? stderr : stdout , " %s", maker.args[i]); + } + info("\n"); + if (should_execute_commands) { + fflush(stderr); + if (*maker.args[0] == '\0') { + status = execlp(maker.cmd, maker.cmd, '\0', (void*)0); + } + else { + status = execvp(maker.cmd, (char* const*)maker.args); + } + if (status == -1) { + printf("Error: %d\n", status); } } - maker = makers[FMAKE_POSIX_MAKEFILE]; - process_build(); + return status; } int main(int argc, char* argv[]) { - process_string(); - return -1; + int i; + for(i = 1; i < argc; i++) { + if (!(argv[i][0] == '-' && argv[i][1] != '\0')) { + printf("Usage: fmake [-l]\n"); + exit(-1); + } + switch(argv[i][1]) { + case '-': + is_accepting_cmd_args = 1; + goto FMAKE_AFTER_ARG_CHECK; + break; + case 'l': + // TODO show better listing + /* list supported build systems */ + for (size_t i = 0; i < (sizeof(makers) / sizeof(maker_config_t)); i++) { + printf("%s\n", makers[i].cmd); + } + break; + case 'a': + should_execute_commands = 1; + break; + } + } +FMAKE_AFTER_ARG_CHECK: + argc = i; + argv = argv + i; + for (size_t i = 0; i < (sizeof(makers) / sizeof(maker_config_t)); i++) { + if (makers[i].check_extension) { + // TODO optimize this code + // FIXME it's not sure if the '.' is found from first or last + // last should be preferred + dir = opendir("./"); + if (dir == NULL) { + perror("fmake:"); + return -1; + } + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_REG) { + char* dot = strrchr(entry->d_name, '.'); + if (dot && strcmp(dot + 1, makers[i].filename) == 0) { + maker = makers[i]; + return process_build(argv); + } + } + } + printf("extension check %s\n", makers[i].filename); + } else { + const char* filename = makers[i].filename; + if (access(filename, F_OK) == 0) { + maker = makers[i]; + return process_build(argv); + } + } + } + maker = makers[0]; + return process_build(argv); } diff --git a/todo.txt b/todo.txt @@ -0,0 +1,21 @@ +fmake uses chains. + +CHAINS +------ +Chains are created for build tools which require the user to execute multiple commands in order to execute the build. + +Example +------- + cmake -Bout -S. -GNinja + cd out + ninja + + +This can be defined in the config.txt as, + +FM_CMAKE->FM_NINJA + +Another example is confiure +FM_AUTORECONF->FM_AUTOCONF +FM_AUTOCONF->FM_MAKE +