# HG changeset patch # User Abscissa # Date 1286142910 14400 # Node ID b080e62b16b45094070e2da68de3492448d4e08b # Parent 96c0fff6897d138e4128aef9050ece8a8320dc67 Enhanced buildscript to simplify compiling for new users diff -r 96c0fff6897d -r b080e62b16b4 build.bat --- a/build.bat Thu Sep 30 14:09:50 2010 +0400 +++ b/build.bat Sun Oct 03 17:55:10 2010 -0400 @@ -1,2 +1,10 @@ @echo off -cls && dmc.exe bridge\bridge.cpp -c && dmd -debug -g @commands.txt && dmd -release -O -inline @commands.txt \ No newline at end of file +rem This has exactly the same semantics as the old buildscript, +rem but supports extra optional command line arguments. +rem Too see the supported args, run: build.bat --help + +rem Pre-build 'buildHelper.d' as a workaround for RDMD issue #4688 +rdmd --build-only -ofbuildHelper -unittest buildHelper.d && buildHelper.exe %* + +rem Old buildscript: +rem cls && dmc.exe bridge\bridge.cpp -c && dmd -debug -g @commands.txt && dmd -release -O -inline @commands.txt diff -r 96c0fff6897d -r b080e62b16b4 build.sh --- a/build.sh Thu Sep 30 14:09:50 2010 +0400 +++ b/build.sh Sun Oct 03 17:55:10 2010 -0400 @@ -1,4 +1,11 @@ #!/bin/sh -#i686-unknown-linux-gnu-g++ -c bridge/bridge.cpp -obridge.o -g++ -c bridge/bridge.cpp -obridge.o -dmd -debug -gc @commands.linux.txt && dmd -release -O -inline @commands.linux.txt #|& head +# This has exactly the same semantics as the old buildscript, +# but supports extra optional command line arguments. +# Too see the supported args, run: ./build.sh --help + +rdmd -unittest buildHelper.d "$@" + +# Old buildscript: +##i686-unknown-linux-gnu-g++ -c bridge/bridge.cpp -obridge.o +#g++ -c bridge/bridge.cpp -obridge.o +#dmd -debug -gc @commands.linux.txt && dmd -release -O -inline @commands.linux.txt #|& head diff -r 96c0fff6897d -r b080e62b16b4 buildHelper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/buildHelper.d Sun Oct 03 17:55:10 2010 -0400 @@ -0,0 +1,334 @@ +// Written in The D Programming Language +/// Script to build DDMD +import std.file; +import std.getopt; +import std.path; +import std.process; +import std.stdio; +import std.string: replace, format; +import std.zip; + +enum dmdVersionDefault = "2.039"; +enum dmdArchiveBaseURL = "http://ftp.digitalmars.com/"; +enum dmdLib = "dmd.lib"; +version(Windows) +{ + enum scriptName = "build.bat"; + enum osSubDir = "windows\\"; + enum configFile = "sc.ini"; + enum execExt = ".exe"; +} +else +{ + enum scriptName = "./build.sh"; + enum osSubDir = "linux/"; + enum configFile = "dmd.conf"; + enum execExt = ""; +} + +string dmdVersion; +string dmdPackage; +string dmdArchive; +string dmBase=""; +bool shouldDownload; + +void doCopy(string from, string to) +{ + from = normFilePath(from); + to = normFilePath(to); + + writefln(`copy "%s" "%s"`, from, to); + copy(from, to); +} + +void doChDir(string dir) +{ + dir = normDirPath(dir); + + writefln(`chdir "%s"`, dir); + chdir(dir); +} + +int doSystem(string cmd) +{ + writeln(cmd); + stdout.flush(); + return system(cmd); +} + +void copyRecurse(string from, string to) +{ + from = normDirPath(from)[0..$-1]; + to = normDirPath(to)[0..$-1]; + version(Windows) + doSystem(`xcopy "%s" "%s" /E /I /Y /Q`.format(from, to)); + else + doSystem(`cp '%s' '%s' -r`.format(from, to)); +} + +void copyAndPatch(string from, string to, void delegate(ref string) patcher) +{ + auto data = cast(string)read(from); + patcher(data); + std.file.write(to, data); +} + +/// makePath("C:\foo\bar\dir") will create "C:\foo\bar\dir" if it doesn't already exist. +/// makePath("C:\foo\bar\dir\") will create "C:\foo\bar\dir" if it doesn't already exist. +void makePath(string dir) +{ + dir = normDirPath(dir)[0..$-1]; + if(!exists(dir)) + mkdirRecurse(dir); +} + +/// makePathTo("C:\foo\bar\file.txt") will create "C:\foo\bar\" if it doesn't already exist. +/// makePathTo("C:\foo\bar\dir\") will create "C:\foo\bar\" if it doesn't already exist. +void makePathTo(string file) +{ + file = normFilePath(file); + auto dir = dirname(file); + makePath(dir); +} + +/// Ensure trailing slash and OS-correct path separators +string normDirPath(string str) +{ + str = normPathSep(str); + if(str.length > 0 && str[$-1] != sep[0]) + str ~= sep; + + return str; +} + +/// Ensure no trailing slash and OS-correct path separators +string normFilePath(string str) +{ + str = normPathSep(str); + if(str.length > 0 && str[$-1] == sep[0]) + str = str[0..$-1]; + + return str; +} + +/// Ensure OS-correct path separators +string normPathSep(string str) +{ + version(Windows) + str = str.replace("/", "\\"); + else + str = str.replace("\\", "/"); + + return str; +} + +unittest +{ + version(Windows) + { + assert(normDirPath ("C:\\a/b\\c/d" ) == "C:\\a\\b\\c\\d\\"); + assert(normDirPath ("C:\\a/b\\c/d\\") == "C:\\a\\b\\c\\d\\"); + assert(normDirPath ("C:\\a/b\\c/d/" ) == "C:\\a\\b\\c\\d\\"); + assert(normFilePath("C:\\a/b\\c/d" ) == "C:\\a\\b\\c\\d" ); + assert(normFilePath("C:\\a/b\\c/d\\") == "C:\\a\\b\\c\\d" ); + } + else + { + assert(normDirPath ("\\a/b\\c/d" ) == "/a/b/c/d/"); + assert(normDirPath ("\\a/b\\c/d\\") == "/a/b/c/d/"); + assert(normDirPath ("\\a/b\\c/d/" ) == "/a/b/c/d/"); + assert(normFilePath("\\a/b\\c/d" ) == "/a/b/c/d" ); + assert(normFilePath("\\a/b\\c/d\\") == "/a/b/c/d" ); + } +} + +bool initialSetup() +{ + writeln("Running initial setup..."); + + // Download dmd zip + if(!exists(dmdArchive) && shouldDownload) + doSystem("wget "~dmdArchiveBaseURL~dmdArchive); + + // Extract dmd zip + writeln("Extracting dmd archive..."); + if(exists("dmd2")) + rmdirRecurse("dmd2"); + auto zip = new ZipArchive(std.file.read(dmdArchive)); + foreach(member; zip.directory) + { + makePathTo(member.name); + std.file.write(member.name, zip.expand(member)); + } + + // Make mars2.c with 'main' hidden + doChDir("dmd2/src/dmd"); + copyAndPatch("mars.c", "mars2.c", (ref string data) { + data = data.replace("int main(int argc, char *argv[])", "int HIDE_main(int argc, char *argv[])"); + }); + + // Apply patch + doChDir("../../.."); + doChDir("dmd2"); + doSystem("patch -p1 --binary < " ~ normFilePath("../dmdpatch.patch")); + + // Setup makefile for dmd.lib + doChDir(".."); + doChDir("dmd2/src/dmd"); + version(Windows) + enum makefile = "win32.mak"; + else + { + enum makefile = "linux_lib.mak"; + doCopy("../../../"~makefile, makefile); + } + + version(Windows) + copyAndPatch(makefile, makefile, (ref string data) { + if(dmBase == "") + { + data = data.replace("\nCC=$(SCROOT)\\bin\\dmc", "\nCC=dmc"); + data = data.replace("\nLIB=$(SCROOT)\\bin\\lib", "\nLIB=lib"); + } + else + data = data.replace("\nD=", "\nD="~dmBase); + }); + + + // Build dmd.lib + version(Windows) + doSystem("make deblib -f"~makefile); + else + doSystem("make -f"~makefile); + doCopy("dmd.lib", "../../../dmd.lib"); + + // Copy and patch config file + doChDir("../../.."); + copyAndPatch( + normFilePath("dmd2/"~osSubDir~"bin/"~configFile), + normFilePath("bin/"~configFile), + (ref string data) { + data = data.replace(normDirPath("../.."), normDirPath("..")); + } + ); + + // Copy std lib + if(exists("src")) + rmdirRecurse("src"); + mkdir("src"); + copyRecurse("dmd2/src/druntime", "src/druntime"); + copyRecurse("dmd2/src/phobos", "src/phobos" ); + copyRecurse("dmd2/"~osSubDir~"lib", "lib" ); + + // Copy linker + doCopy("dmd2/"~osSubDir~"bin/link"~execExt, "bin/link"~execExt); + + return true; +} + +int main(string[] args) +{ + endOfOptions = ""; + bool help; + bool shouldSetup; + bool debugOnly; + bool releaseOnly; + dmdVersion = dmdVersionDefault; + getopt( + args, + std.getopt.config.caseSensitive, + "setup", &shouldSetup, + "debug|d", &debugOnly, + "release|r", &releaseOnly, + "ver", &dmdVersion, + "download", &shouldDownload, + "dmbase", &dmBase, + "help|h|H|?", &help + ); + + dmdPackage = "dmd."~dmdVersion; + dmdArchive = dmdPackage~".zip"; + + auto helpMsg = +`This script will compile DDMD + +Note that this script must be run from the main DDMD directory. + +Also, make sure you have GNU patch installed and current versions +of DMC and DMD (D2) on the PATH. (DMC is only needed on Windows.) + +Usage: + `~scriptName~` [options...] + + --help,-h,-H,-? Display this help message + --debug,-d Only build debug version + --release,-r Only build release version + --ver={ver} Base DDMD off specific DMD version (default: `~dmdVersionDefault~`) + --setup Run initial setup + --download If running initial setup and the dmd zip doesn't exist, + use wget to download it + --dmbase={path} Path to directory containing 'dm' for building dmd.lib + (Optional if --setup is used, otherwise ignored) +`; + + // Assume the user meant they wanted both. + if(debugOnly && releaseOnly) + { + debugOnly = releaseOnly = false; + } + + if( + help || + ( shouldSetup && !exists(dmdArchive) && !shouldDownload ) || + ( shouldSetup && exists(dmdArchive) && !isfile(dmdArchive) ) + ) + { + write(helpMsg); + return 1; + } + + if( !shouldSetup && (!exists(dmdLib) || !isfile(dmdLib)) ) + { + auto needSetupMsg = +`'`~dmdLib~`' has not been built so you need to run the initial setup: + +If you have GNU wget installed, just run: + `~scriptName~` --setup --download + +If you don't have wget, download a copy of `~dmdArchive~` to this +directory. It can be obtained from: + `~dmdArchiveBaseURL~dmdArchive~` +Then run: + `~scriptName~` --setup + +For a full list of options, run: + `~scriptName~` --help +`; + + write(needSetupMsg); + return 1; + } + + if(shouldSetup) + { + if(!initialSetup()) + return 1; + } + + int ret=0; + version(Windows) + { + system("cls"); + if(ret == 0) ret = doSystem(r"dmc.exe bridge\bridge.cpp -c"); + if(!releaseOnly) if(ret == 0) ret = doSystem(r"dmd -debug -g @commands.txt"); + if(!debugOnly) if(ret == 0) ret = doSystem(r"dmd -release -O -inline @commands.txt"); + } + else + { + if(ret == 0) ret = doSystem("g++ -c bridge/bridge.cpp -obridge.o"); + if(!releaseOnly) if(ret == 0) ret = doSystem("dmd -debug -gc @commands.linux.txt"); + if(!debugOnly) if(ret == 0) ret = doSystem("dmd -release -O -inline @commands.linux.txt"); + } + + return ret; +}