diff --git a/.gitignore b/.gitignore index 5324768..86d6c52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /pmake* *.o +*.a /Makefile /*.make /src/compiler/*.c diff --git a/src/compiler/gcc.js b/src/compiler/gcc.js index 3cfbf8e..49c6de5 100644 --- a/src/compiler/gcc.js +++ b/src/compiler/gcc.js @@ -4,5 +4,8 @@ const GCC = new pmake.POSIXCompiler(); GCC.target = "GCC"; GCC.description = "GCC/Clang"; +GCC.cc = "gcc"; +GCC.cxx = "g++"; + pmake.register(GCC); })(); diff --git a/src/js.c b/src/js.c index 7aea3f6..ed5fee9 100644 --- a/src/js.c +++ b/src/js.c @@ -34,13 +34,13 @@ int js_init(int argc, char** argv) { js_fs_init(); if(duk_peval_lstring(js_ctx, js_core, js_core_len) != 0) { - printf("error: js/core.js: %s\n", duk_safe_to_string(js_ctx, -1)); + printf("JS error: js/core.js: %s\n", duk_safe_to_string(js_ctx, -1)); return 1; } #define X(name, prefix) \ if(duk_peval_lstring(js_ctx, prefix##_##name, prefix##_##name##_len) != 0) { \ - printf("error: %s: %s\n", #prefix "/" #name ".js", duk_safe_to_string(js_ctx, -1)); \ + printf("JS error: %s: %s\n", #prefix "/" #name ".js", duk_safe_to_string(js_ctx, -1)); \ return 1; \ } JS; @@ -49,7 +49,7 @@ int js_init(int argc, char** argv) { if((st = js_run("pmake.js")) != 0) return st; if(duk_peval_lstring(js_ctx, js_cmdline, js_cmdline_len) != 0) { - printf("error: js/cmdline.js: %s\n", duk_safe_to_string(js_ctx, -1)); + printf("JS error: js/cmdline.js: %s\n", duk_safe_to_string(js_ctx, -1)); return 1; } @@ -74,7 +74,7 @@ int js_run(const char* file) { fread(buffer, 1, len, f); if(duk_peval_lstring(js_ctx, buffer, len) != 0) { - printf("error: %s: %s\n", file, duk_safe_to_string(js_ctx, -1)); + printf("JS error: %s: %s\n", file, duk_safe_to_string(js_ctx, -1)); free(buffer); fclose(f); diff --git a/src/js/cmdline.js b/src/js/cmdline.js index 334d4ee..b873bf3 100644 --- a/src/js/cmdline.js +++ b/src/js/cmdline.js @@ -1,5 +1,7 @@ const PADDING = 24; +pmake.compiler = pmake.compilers["GCC"]; + for(var i = 1; i < ARGV.length; i++) { if(ARGV[i] == "--help") { var keys; @@ -48,35 +50,104 @@ for(var i = 1; i < ARGV.length; i++) { } process.exit(0); } else if(ARGV[i].startsWith("--")) { - console.log("Error: invalid option '" + ARGV[i].substr(2) + "'"); - process.exit(1); + var args = ARGV[i].split("="); + + args[0] = args[0].substr(2); + + if(pmake.options[args[0]]) { + var opts = pmake.options[args[0]].options; + + if(opts) { + if((typeof opts) == "function") opts = opts(); + } + + if(opts && !args[1]) { + console.log("Error: no value specified for option '" + args[0] + "'"); + process.exit(1); + } else if(opts && !opts.includes(args[1])) { + console.log("Error: invalid value '" + args[1] + "' for option '" + args[0] + "'"); + process.exit(1); + } else if(opts) { + pmake.options[args[0]].action(args[1]); + } else { + pmake.options[args[0]].action(); + } + } else { + console.log("Error: invalid option '" + args[0] + "'"); + process.exit(1); + } } } (function() { function make(output) { + const targets = pmake.resolveProjects(pmake.projects); + + function target_map(x) { + return x.target; + } + var mk = new pmake.File(output, "w"); mk.write("# This file was auto-generated. Do not edit manually!\n"); mk.write("\n"); mk.write("SILENT = @\n"); + mk.write("RM = rm -f\n"); mk.write("\n"); + mk.write(".PHONY: all clean\n"); if(output == "Makefile") { - var targets = pmake.resolveProjects(pmake.projects); + mk.write(".PHONY: " + targets.map(target_map).join(" ") + "\n"); + mk.write("\n"); + + mk.write("MAKEARGS$(.TARGET) = --no-print-directory"); + mk.write("\n"); + + mk.write("all: " + pmake.projects.map(target_map).join(" ") + "\n"); + mk.write("\n"); + for(var i = 0; i < targets.length; i++) { - mk.write(targets[i].target + ":\n"); - mk.write(" @$(MAKE) -f " + targets[i].target + ".make\n"); + mk.write(targets[i].target + ": " + pmake.resolveProjects(targets[i].libraries).map(target_map).join(" ") + "\n"); + mk.write(" @$(MAKE) $(MAKEARGS) -f " + targets[i].target + ".make\n"); mk.write("\n"); } + + mk.write("clean:\n"); + for(var i = 0; i < targets.length; i++) { + mk.write(" @$(MAKE) $(MAKEARGS) -f " + targets[i].target + ".make clean\n"); + } } else { + function target_filter(x) { + return x.target == output.replace(/\.make$/, ""); + } + + const self = targets.filter(target_filter)[0]; + const objs = self.sources.map(pmake.compiler.output); + + mk.write("\n"); + + mk.write("all: " + pmake.compiler.output(self) + "\n"); + mk.write("\n"); + + mk.write(pmake.compiler.output(self) + ": " + objs.join(" ") + "\n"); + mk.write(" " + pmake.compiler.link(self, objs) + "\n"); + mk.write("\n"); + + for(var i = 0; i < objs.length; i++) { + mk.write(objs[i] + ": " + self.sources[i] + "\n"); + mk.write(" " + pmake.compiler.compile(self, objs[i], self.sources[i]) + "\n"); + mk.write("\n"); + } + + mk.write("clean: \n"); + mk.write(" $(SILENT)$(RM) " + pmake.compiler.output(self) + " *.o *.exe *.a *.lib *.so *.dll\n"); } mk.close(); } make("Makefile"); -var targets = pmake.resolveProjects(pmake.projects); +const targets = pmake.resolveProjects(pmake.projects); for(var i = 0; i < targets.length; i++) { make(targets[i].target + ".make"); } diff --git a/src/js/core.js b/src/js/core.js index 5582dba..04a291d 100644 --- a/src/js/core.js +++ b/src/js/core.js @@ -1,12 +1,37 @@ +if(!Array.prototype.includes) { + Array.prototype.includes = function(v) { + for(var i = 0; i < this.length; i++) { + if(this[i] == v) return true; + } + + return false; + }; +} + +if(!Array.prototype.flat) { + Array.prototype.flat = function() { + const flat = function(acc, cur) { + return acc.concat(cur); + }; + + return this.reduce(flat, []); + }; +} + const pmake = { VERSION : "0.0", compilers : {}, options : {}, + options_category : {}, projects : [] }; pmake.inherit = function(base, call) { - const func = (function() {}); + const func = (function() { + base.apply(this, arguments); + + if(call) call.apply(this, arguments); + }); Object.setPrototypeOf(func.prototype, base.prototype); @@ -25,47 +50,159 @@ pmake.inherited = function(base, parent) { return false; }; -pmake.register = function(base) { - if(base instanceof pmake.Compiler) { - pmake.compilers[base.target] = base; - } else if(base instanceof pmake.Option) { - if(!pmake.options[base.category]) pmake.options[base.category] = {}; +pmake.register = function(target) { + if(target instanceof pmake.Compiler) { + pmake.compilers[target.target] = target; + } else if(target instanceof pmake.Option) { + if(!pmake.options_category[target.category]) pmake.options_category[target.category] = {}; - pmake.options[base.category][base.target] = base; - } else if(base instanceof pmake.Project) { - pmake.projects.push(base); + pmake.options_category[target.category][target.target] = target; + pmake.options[target.target] = target; + } else if(target instanceof pmake.Project) { + pmake.projects.push(target); } }; -pmake.resolveProjects = function(arr) { +pmake.resolveProjects = function(arr, if_str) { var r = []; for(var i = 0; i < arr.length; i++) { - if(arr[i] instanceof pmake.Project) { - r = r.concat(pmake.resolveProjects(arr[i].libraries)); - r.push(arr[i]); + var proj = arr[i]; + + if((typeof proj) == "string" && pmake.projects[proj]) { + proj = pmake.projects[proj]; + } + + if(proj instanceof pmake.Project) { + r = r.concat(pmake.resolveProjects(proj.libraries)); + r.push(proj); + } else if((typeof proj) == "string" && if_str) { + r.push(if_str(proj)); } } + r = r.filter(function(item, pos) { + return r.indexOf(item) == pos; + }); + return r; }; -(function() { -const Compiler = function() {}; +pmake.isCxx = function(src) { + return src.endsWith(".cc") || src.endsWith(".cxx") || src.endsWith(".cpp"); +}; -const POSIXCompiler = pmake.inherit(Compiler); +pmake.includes = function(target) { + function includes(x) { + return x.includes; + } + + const targets = pmake.resolveProjects(target.libraries); + + return targets.map(includes).flat(); +}; + +pmake.libs = function(target, if_str) { + function libs(x) { + if(x instanceof pmake.LibraryProject) { + return x.libraries.concat([ pmake.compiler.output(x) ]); + } else if((typeof x) == "string") { + return [ x ]; + } + + return []; + } + + const targets = pmake.resolveProjects(target.libraries, if_str); + + return targets.map(libs).flat(); +}; + +(function() { +const Compiler = function() { + this.prefix = ""; +}; + +Compiler.prototype.output = function(target) { + if((typeof target) == "string") { + if(target.endsWith(".c")) return target.replace(/\.c$/, ".o"); + } else if(target instanceof pmake.LibraryProject) { + return "lib" + target.output + ".a"; + } else if(target instanceof pmake.ExecutableProject) { + return target.output; + } + + return target.output; +}; + +const POSIXCompiler = pmake.inherit(Compiler, function(target) { + this.ar = "ar"; + this.cc = "cc"; + this.cxx = "c++"; +}); + +POSIXCompiler.prototype.compile = function(target, obj, src) { + function add_i(x) { + return "-I" + x; + } + + const includes = pmake.includes(target).map(add_i) + + if(includes.length > 0) includes = " " + includes.join(" "); + + if(src.endsWith(".c")) { + return this.prefix + this.cc + includes + " -c -o " + obj + " " + src; + } else if(is_cxx(src)) { + return this.prefix + this.cxx + includes + " -c -o " + obj + " " + src; + } + + return ""; +} + + POSIXCompiler.prototype.link = function(target, files) { + if(target instanceof pmake.LibraryProject) { + return this.prefix + this.ar + " rcs " + this.output(target) + " " + files.join(" "); + } else if(target instanceof pmake.ExecutableProject) { + var c = ""; + var cxx = target.sources.filter(pmake.isCxx); + if(!cxx || cxx.length == 0) { + c = this.prefix + this.cc; + } else { + c = this.prefix + this.cxx; + } + + function add_l(x) { + return "-l" + x; + } + + return this.prefix + c + " -o " + this.output(target) + " " + files.join(" ") + " " + pmake.libs(target, add_l).join(" "); + } + + return ""; +} const Option = function() {}; const Project = function(target) { this.target = target; - this.files = []; + this.sources = []; + this.includes = []; this.libraries = []; + this.output = target; }; Project.prototype.generate = function() { }; +const LibraryProject = pmake.inherit(Project, function(target) { +}); + +const ExecutableProject = pmake.inherit(Project, function(target) { +}); + +const InterfaceProject = pmake.inherit(Project, function(target) { +}); + const File = function(filename, mode) { this.fp = fs.open(filename, mode); }; @@ -78,11 +215,14 @@ File.prototype.close = function() { fs.close(this.fp); }; -pmake.Compiler = Compiler; -pmake.POSIXCompiler = POSIXCompiler; -pmake.Option = Option; -pmake.Project = Project; -pmake.File = File; +pmake.Compiler = Compiler; +pmake.POSIXCompiler = POSIXCompiler; +pmake.Option = Option; +pmake.Project = Project; +pmake.LibraryProject = LibraryProject; +pmake.ExecutableProject = ExecutableProject; +pmake.InterfaceProject = InterfaceProject; +pmake.File = File; })(); /*** Option ***/ @@ -98,5 +238,9 @@ Compiler.options = function() { return Object.keys(pmake.compilers); }; +Compiler.action = function(value) { + pmake.compiler = pmake.compilers[value]; +}; + pmake.register(Compiler); })();