fmake

make any project just by typing `fmake`

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.
6 files changed, 154 insertions(+), 62 deletions(-)
M
LICENSE
|
0
M
Makefile
|
9
++++++---
M
README
|
12
+++++++++---
M
config.h
|
71
+++++++++++++++++++++++++++++++----------------------------------------
M
fmake.c
|
103
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
A
todo.txt
|
21
+++++++++++++++++++++
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
+