#include "umake.h" #ifndef bmYEAR #include #endif bool SilentMode; String GetUmkFile(const char *fn) { if(FileExists(fn)) return NormalizePath(fn); if(DirectoryExists(fn) || *fn == '.') return Null; String h = ConfigFile(fn); if(FileExists(h)) return h; String cfgdir = GetFileFolder(GetFileFolder(ConfigFile("x"))); ONCELOCK PutVerbose("Config directory: " << cfgdir); return GetFileOnPath(fn, cfgdir + "/umk" + ';' + cfgdir + "/theide" + ';' + cfgdir + "/ide" + ';' + GetHomeDirectory() + ';' + GetFileFolder(GetExeFilePath())); } String GetBuildMethodPath(String method) { if(GetFileExt(method) != ".bm") method << ".bm"; return GetUmkFile(method); } String Ide::GetDefaultMethod() { return "GCC"; } String ReplaceMethodDir(String paths, const String& method_dir) { constexpr const char* METHOD_DIR = "${METHOD_DIR}"; if (paths.Find(METHOD_DIR) == -1) { return paths; } paths.Replace(METHOD_DIR, method_dir); return paths; } VectorMap Ide::GetMethodVars(const String& method) { VectorMap map; LoadVarFile(GetMethodName(method), map); const String method_dir = GetFileFolder(method); const Vector categories_with_method_dir = {"PATH", "INCLUDE", "LIB" }; for (const auto& category : categories_with_method_dir) { map.GetAdd(category) = ReplaceMethodDir(map.Get(category), method_dir); } return map; } String Ide::GetMethodName(const String& method) { return GetBuildMethodPath(method); } void Puts(const char *s) { if(!SilentMode) Cout() << s; } String GetAndroidSDKPath() { return String(); } #ifdef flagMAIN String GenerateVersionNumber() { #ifdef bmGIT_REVCOUNT return AsString(atoi(bmGIT_REVCOUNT) + 2270); #endif return ""; } CONSOLE_APP_MAIN { SetConfigName("theide"); #ifdef PLATFORM_POSIX setlinebuf(stdout); CreateBuildMethods(); #endif Ide ide; SetTheIde(&ide); ide.console.SetSlots(CPU_Cores()); ide.console.console = true; ide.debug.def.blitz = ide.release.def.blitz = 0; ide.debug.def.debug = 2; ide.release.def.debug = 0; ide.debug.package.Clear(); ide.release.package.Clear(); ide.debug.linkmode = ide.release.linkmode = 0; ide.release.createmap = ide.debug.createmap = false; ide.targetmode = 0; ide.use_target = false; ide.makefile_svn_revision = false; bool clean = false; bool makefile = false; bool ccfile = false; bool deletedir = true; int exporting = 0; bool run = false; bool auto_hub = false; bool update_hub = false; bool only_hub = false; String hub_dir; String out_dir; bool flatpak_build = !GetEnv("FLATPAK_ID").IsEmpty(); String mkf; Vector param, runargs; const Vector& args = CommandLine(); for(int i = 0; i < args.GetCount(); i++) { String a = args[i]; if(a.StartsWith("--")) { String ar = a.Right(a.GetCount() - 2); if(ar == "hub-dir") { if(i + 1 >= args.GetCount()) { Puts("UppHub directory not specified"); SetExitCode(7); return; } hub_dir = args[++i]; } else if(ar == "hub-only") { only_hub = true; } else if(ar == "out-dir") { if(i + 1 >= args.GetCount()) { Puts("Output directory not specified"); SetExitCode(7); return; } out_dir = args[++i]; } else { Puts(String("Unrecognized parameter \"") + a + "\"."); SetExitCode(7); return; } } else if(*a == '-') { for(const char *s = ~a + 1; *s; s++) switch(*s) { case 'a': clean = true; break; case 'r': ide.targetmode = 1; break; case 'm': ide.release.createmap = ide.debug.createmap = true; break; case 'b': ide.release.def.blitz = ide.debug.def.blitz = 1; break; case 's': ide.debug.linkmode = ide.release.linkmode = 1; break; case 'd': ide.debug.def.debug = 0; break; case 'S': ide.debug.linkmode = ide.release.linkmode = 2; break; case 'v': ide.console.verbosebuild = true; break; case 'l': SilentMode = true; break; case 'x': exporting = 1; break; case 'X': exporting = 2; break; case 'k': deletedir = false; break; case 'u': ide.use_target = true; break; case 'j': ccfile = true; break; case 'h': auto_hub = true; break; case 'U': update_hub = true; break; case 'M': { makefile = true; if(s[1] == '=') { mkf = NormalizePath(s + 2); PutVerbose("Generating Makefile: " + mkf); goto endopt; } else PutVerbose("Generating Makefile"); break; } case 'H': { int n = 0; while(IsDigit(s[1])) { n = 10 * n + s[1] - '0'; s++; } if(!n) n = CPU_Cores(); n = minmax(n, 1, 256); PutVerbose("Hydra threads: " + AsString(n)); ide.console.SetSlots(n); break; } default: SilentMode = false; Puts("Invalid build option(s)"); SetExitCode(3); return; } endopt:; } else if(*a == '+') ide.mainconfigparam = Filter(~a + 1, [](int c) { return c == ',' ? ' ' : c; }); else if(*a == '!') { run = true; for(int j = i + 1; j < args.GetCount(); j++) runargs.Add(args[j]); if(runargs) PutVerbose("Set to execute the result with args: " << Join(runargs, " ")); else PutVerbose("Set to execute the result"); break; } else param.Add(a); } UppHubSetupDirForUmk(hub_dir, auto_hub); if(param.GetCount() >= 2) { String v = GetUmkFile(param[0] + ".var"); if(IsNull(v)) { #ifdef PLATFORM_POSIX Vector h = Split(param[0], [](int c) { return c == ':' || c == ',' ? c : 0; }); #else Vector h = Split(param[0], ','); #endif for(int i = 0; i < h.GetCount(); i++) h[i] = GetFullPath(TrimBoth(h[i])); String x = Join(h, ";"); SetVar("UPP", x, false); PutVerbose("Inline assembly: " + x); if(out_dir.IsEmpty()) { if(flatpak_build) out_dir = GetExeFolder() + DIR_SEPS + ".cache" + DIR_SEPS + "upp.out"; else out_dir = GetDefaultUppOut(); } if(!IsFullPath(out_dir)) out_dir = GetCurrentDirectory() + DIR_SEPS + out_dir; if(!RealizeDirectory(out_dir)) { Puts("Invalid output directory \"" + out_dir + "\"."); SetExitCode(8); return; } SetVar("OUTPUT", out_dir, false); } else { if(!LoadVars(v)) { Puts("Invalid assembly\n"); SetExitCode(2); return; } PutVerbose("Assembly file: " + v); PutVerbose("Assembly: " + GetVar("UPP")); } PutVerbose("Output directory: " + GetUppOut()); ide.main = param[1]; v = SourcePath(ide.main, GetFileTitle(ide.main) + ".upp"); PutVerbose("Main package: " + v); if(!FileExists(v)) { Puts("Package " + ide.main + " does not exist\n"); SetExitCode(2); return; } if(auto_hub || update_hub) { if(!UppHubAuto(ide.main)) { SetExitCode(6); return; } if(update_hub) UppHubUpdate(ide.main); } if(only_hub) { int exit_code = 0; if(!auto_hub && !update_hub) { exit_code = 6; Puts("The --hub-only option was specified, but UppHub mode instruction are " "missing. Please ensure you include the -U or -h flag for the required " "UppHub mode configuration.\n"); } SetExitCode(exit_code); return; } ide.wspc.Scan(ide.main); const Workspace& wspc = ide.IdeWorkspace(); if(!wspc.GetCount()) { Puts("Empty assembly\n"); SetExitCode(4); return; } Index missing; for(int i = 0; i < wspc.GetCount(); i++) { String p = wspc[i]; if(!FileExists(PackageFile(p))) missing.FindAdd(p); } if(missing.GetCount()) { Puts("Missing package(s): " << Join(missing.GetKeys(), " ") << "\n"); SetExitCode(5); return; } if(IsNull(ide.mainconfigparam)) { const Array& f = wspc.GetPackage(0).config; if(f.GetCount()) ide.mainconfigparam = f[0].param; } PutVerbose("Build flags: " << ide.mainconfigparam); String m = 2 < param.GetCount() ? param[2] : "CLANG"; String bp = GetBuildMethodPath(m); PutVerbose("Build method: " + bp); if(bp.GetCount() == 0) { SilentMode = false; Puts("Invalid build method\n"); SetExitCode(3); return; } if(3 < param.GetCount()) { ide.debug.target_override = ide.release.target_override = true; ide.debug.target = ide.release.target = NormalizePath(param[3]); PutVerbose("Target override: " << ide.debug.target); } ide.method = bp; if(ccfile) { ide.SaveCCJ(GetFileDirectory(PackageFile(ide.main)) + "compile_commands.json", false); SetExitCode(0); return; } if(clean) ide.Clean(); if(exporting) { mkf = GetFullPath(mkf); Cout() << mkf << '\n'; RealizeDirectory(mkf); if(makefile) ide.ExportMakefile(mkf); else ide.ExportProject(mkf, exporting == 2, deletedir); } else if(makefile) { ide.SaveMakeFile(IsNull(mkf) ? "Makefile" : mkf, false); SetExitCode(0); } else if(ide.Build()) { SetExitCode(0); if(run) { Vector args; Vector> buffer; auto Add = [&](const String& s) { auto& b = buffer.Add(); b.Alloc(s.GetCount() + 1); memcpy(b, s, s.GetCount() + 1); args.Add(b); }; Add(ide.target); for(const String& s : runargs) Add(s); args.Add(NULL); SetExitCode((int)execv(ide.target, args.begin())); } } else SetExitCode(1); } else { String version = GenerateVersionNumber(); Puts("umk (U++MaKe) " + version + "\n\n" "Usage:\n" " umk assembly package [build_method] [--hub-dir dir] [--hub-only] [-options] [+flags] [out]\n" " [! [runarg]..]\n\n" "Arguments:\n" " assembly - is a direct set of package nest directories relative to working directory that\n" " represent U++ assembly separated by ','.\n" " package - is the main package (a program to build).\n" " build_method - is build method that is to be used to build the resulting executable. If not\n" " specified, CLANG build method is assumed. Note that in POSIX, umk automatically\n" " creates CLANG and GCC build methods if they do not exist.\n" " Optional parameters:\n" " --hub-dir - specifies the directory where UppHub packages should be downloaded, using\n" " the second parameter, dir, to set the path.\n" " --hub-only - instructs UMK to handle only the logic related to UppHub.\n" " --out-dir - specifies the directory where UMK stores build artifacts,\n" " using the second parameter, dir, to define the path.\n" " Additional options [-options for example -brU]:\n" " a - rebuild all.\n" " b - use BLITZ.\n" " l - silent mode.\n" " u - use target directory.\n" " m - create a map file.\n" " r - release mode.\n" " d - debug mode without debug symbols.\n" " s - use shared libraries.\n" " S - use shared libraries and build as shared libraries.\n" " v - be verbose\n" " M - create makefile (to file Makefile)\n" " M=makefile - create makefile with given name.\n" " Hn - number of threads used for build. Default is number of logical cores available.\n" " h - enables downloading missing packages from UppHub. This command removes any other\n" " UppHub packages if exists.\n" " U - Install missing packages from UppHub and update all UppHub nests to the latest versions.\n" " Do not delete any existing UppHub packages.\n" " j - generate compile_commands.json\n" " x - export projects sources and documentation\n" " X - export entire project\n" " k - delete target directory before project export\n\n" " If none of the above options are provided, a debug build with symbols will be executed\n" " by default.\n" " flags - are compilation flags. If flags are not specified, the first main configuration\n" " entry in .upp file is used by default. Use commas to chain multiple flags,\n" " such as +GUI,SHARED.\n" " out - overrides output name, file or directory.\n" " ! - means the the resulting binary should be also executed after successful build, using\n" " optional arguments after ! as its arguments.\n" "\nExamples:\n" " Basic:\n" " umk examples Bombs CLANG -ab +GUI,SHARED ~/bombs\n" " umk ~/upp.src/examples,~/upp.src/uppsrc Bombs ~/GCC.bm -rv +GUI,SHARED ~/bin\n" " umk ./,3p/uppsrc UppTerm 3p/umk/CLANG.bm --hub-dir 3p/hub -brU +GUI,SHARED build/UppTerm\n\n" " Below is an example of how to download UppHub dependencies without triggering a build:\n" " umk ./,3p/uppsrc UppTerm 3p/umk/CLANG.bm --hub-dir 3p/hub --hub-only -U\n\n" "See https://www.ultimatepp.org/app$ide$umk$en-us.html for more details.\n"); } } #endif