1 #!/usr/bin/env rdmd 2 /******************************************************************************* 3 4 Standalone build script for DUB 5 6 This script can be called from anywhere, as it deduces absolute paths 7 based on the script's location in the source tree. 8 9 Invoking it while making use of all the options would look like this: 10 DMD=ldmd2 GITVER="1.2.3" ./build.d -O -inline 11 The GITVER environment variable determines the version of 12 the repository to build against. 13 14 Copyright: D Language Foundation 15 Authors: Mathias 'Geod24' Lang 16 License: MIT 17 18 *******************************************************************************/ 19 module build; 20 21 private: 22 23 import std.algorithm; 24 static import std.file; 25 import std.format; 26 import std.path; 27 import std.process; 28 import std.stdio; 29 import std.string; 30 31 /// Root of the `git` repository 32 immutable RootPath = __FILE_FULL_PATH__.dirName; 33 /// Path to the version file 34 immutable VersionFilePath = RootPath.buildPath("source", "dub", "version_.d"); 35 /// Path to the file containing the files to be built 36 immutable SourceListPath = RootPath.buildPath("build-files.txt"); 37 /// Path at which the newly built `dub` binary will be 38 version (Windows) { 39 immutable DubBinPath = RootPath.buildPath("bin", "dub.exe"); 40 } else { 41 immutable DubBinPath = RootPath.buildPath("bin", "dub"); 42 } 43 44 // Flags for DMD 45 immutable OutputFlag = "-of" ~ DubBinPath; 46 immutable IncludeFlag = "-I" ~ RootPath.buildPath("source"); 47 immutable DefaultDFLAGS = [ "-g", "-O", "-w" ]; 48 49 50 /// Entry point 51 int main(string[] args) 52 { 53 // This does not have a 'proper' CLI interface, as it's only used in 54 // special cases (e.g. package maintainers can use it for bootstrapping), 55 // not for general / everyday usage by newcomer. 56 // So the following is just an heuristic / best effort approach. 57 if (args.canFind("--help", "/?", "-h")) 58 { 59 writeln("USAGE: ./build.d [compiler args (default:", DefaultDFLAGS, "]"); 60 writeln(); 61 writeln(" In order to build DUB, a version module must first be generated."); 62 writeln(" If the GITVER environment variable is present, it will be used to generate the version module."); 63 writeln(" Otherwise this script will look for a pre-existing version module."); 64 writeln(" If no GITVER is provided and no version module exists, `git describe` will be called"); 65 writeln(" Build flags can be provided as arguments."); 66 writeln(" LDC or GDC can be used by setting the `DMD` value to " ~ 67 "`ldmd2` and `gdmd` (or their path), respectively."); 68 return 1; 69 } 70 71 immutable dubVersion = environment.get("GITVER", ""); 72 if (!writeVersionFile(dubVersion)) 73 return 1; 74 75 immutable dmd = getCompiler(); 76 if (!dmd.length) return 1; 77 const dflags = args.length > 1 ? args[1 .. $] : DefaultDFLAGS; 78 79 // Compiler says no to immutable (because it can't handle the appending) 80 const command = [ 81 dmd, 82 OutputFlag, IncludeFlag, 83 "-version=DubUseCurl", "-version=DubApplication", 84 ] ~ dflags ~ [ "@build-files.txt" ]; 85 86 writeln("Building dub using ", dmd, " (dflags: ", dflags, "), this may take a while..."); 87 auto proc = execute(command); 88 if (proc.status != 0) 89 { 90 writeln("Command `", command, "` failed, output was:"); 91 writeln(proc.output); 92 return 1; 93 } 94 95 writeln("DUB has been built as: ", DubBinPath); 96 version (Posix) 97 writeln("You may want to run `sudo ln -s ", DubBinPath, " /usr/local/bin` now"); 98 else version (Windows) 99 writeln("You may want to add the following entry to your PATH " ~ 100 "environment variable: ", DubBinPath); 101 return 0; 102 } 103 104 /** 105 Generate the version file describing DUB's version / commit 106 107 Params: 108 dubVersion = User provided version file. Can be `null` / empty, 109 in which case the existing file (if any) takes precedence, 110 or the version is infered with `git describe`. 111 A non-empty parameter will always override the existing file. 112 */ 113 bool writeVersionFile(string dubVersion) 114 { 115 if (!dubVersion.length) 116 { 117 if (std.file.exists(VersionFilePath)) 118 { 119 writeln("Using pre-existing version file. To force a rebuild, " ~ 120 "provide an explicit version (first argument) or remove: ", 121 VersionFilePath); 122 return true; 123 } 124 125 auto pid = execute(["git", "describe"]); 126 if (pid.status != 0) 127 { 128 writeln("Could not determine version with `git describe`. " ~ 129 "Make sure 'git' is installed and this is a git repository. " ~ 130 "Alternatively, you can provide a version explicitly via the " ~ 131 "`GITVER environment variable or pass it as the first " ~ 132 "argument to this script"); 133 return false; 134 } 135 dubVersion = pid.output.strip(); 136 } 137 138 try 139 { 140 std.file.write(VersionFilePath, q{ 141 /** 142 DUB version file 143 144 This file is auto-generated by 'build.d'. DO NOT EDIT MANUALLY! 145 */ 146 module dub.version_; 147 148 enum dubVersion = "%s"; 149 }.format(dubVersion)); 150 writeln("Wrote version_.d` file with version: ", dubVersion); 151 return true; 152 } 153 catch (Exception e) 154 { 155 writeln("Writing version file to '", VersionFilePath, "' failed: ", e.msg); 156 return false; 157 } 158 } 159 160 /** 161 Detect which compiler is available 162 163 Default to DMD, then LDC (ldmd2), then GDC (gdmd). 164 If none is in the PATH, an error will be thrown. 165 166 Note: 167 It would be optimal if we could get the path of the compiler 168 invoking this script, but AFAIK this isn't possible. 169 */ 170 string getCompiler () 171 { 172 auto env = environment.get("DMD", ""); 173 // If the user asked for a compiler explicitly, respect it 174 if (env.length) 175 return env; 176 177 static immutable Compilers = [ "dmd", "ldmd2", "gdmd" ]; 178 foreach (bin; Compilers) 179 { 180 try 181 { 182 auto pid = execute([bin, "--version"]); 183 if (pid.status == 0) 184 return bin; 185 } 186 catch (Exception e) 187 continue; 188 } 189 writeln("No compiler has been found in the PATH. Attempted values: ", Compilers); 190 writeln("Make sure one of those is in the PATH, or set the `DMD` variable"); 191 return null; 192 }