diff --git a/.travis.yml b/.travis.yml index b1a5d78..c8073d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,16 @@ language: d +sudo: false -env: - # dmd: 2.064 - 2.066 - - DC_NAME=DMD DC_URL=http://downloads.dlang.org/releases/2013/ DC_ARCHIVE=dmd_2.064.2-0_amd64.deb DC_BIN=dmd - - DC_NAME=DMD DC_URL=http://downloads.dlang.org/releases/2014/ DC_ARCHIVE=dmd_2.065.0-0_amd64.deb DC_BIN=dmd - - DC_NAME=DMD DC_URL=http://downloads.dlang.org/releases/2014/ DC_ARCHIVE=dmd_2.066.0-0_amd64.deb DC_BIN=dmd - # ldc: Latest (0.14.0 / FE 2.065.0 ATM) - - DC_NAME=LDC DC_URL=https://github.com/ldc-developers/ldc/releases/download/v0.14.0/ DC_ARCHIVE=ldc2-0.14.0-linux-x86_64.tar.gz DC_BIN=/usr/local/ldc2-0.14.0-linux-x86_64/bin/ldc2 - # gdc: Latest (4.9.0 / FE 2.065.0 ATM) - - DC_NAME=GDC DC_URL=http://gdcproject.org/downloads/binaries/x86_64-linux-gnu/ DC_ARCHIVE=native_2.065_gcc4.9.0_a8ad6a6678_20140615.tar.xz DC_BIN=/usr/local/x86_64-gdcproject-linux-gnu/bin/gdc - -install: - # Install release version of DUB for bootstrapping - - BOOTSTRAP_DUB_VER=0.9.21 - - BOOTSTRAP_DUB=dub-${BOOTSTRAP_DUB_VER}-linux-x86_64 - - wget http://code.dlang.org/files/${BOOTSTRAP_DUB}.tar.gz - - sudo tar -C /usr/local/bin -zxf ${BOOTSTRAP_DUB}.tar.gz - - # Install compiler - - wget ${DC_URL}${DC_ARCHIVE} - - if [[ $DC_NAME == "DMD" ]]; then sudo dpkg -i ${DC_ARCHIVE} || true; sudo apt-get -y update; sudo apt-get -fy install; sudo dpkg -i ${DC_ARCHIVE}; fi - - if [[ $DC_NAME != "DMD" ]]; then sudo tar xf ${DC_ARCHIVE} -C /usr/local/; fi +d: + - dmd-2.064.2 + - dmd-2.065.0 + - dmd-2.066.1 + - ldc-0.14.0 + - ldc-0.15.1 + - gdc-4.9.0 script: - - dub test --compiler=${DC_BIN} -c library-nonet - - dub build --compiler=${DC_BIN} - - DUB=`pwd`/bin/dub COMPILER=${DC_BIN} test/run-unittest.sh + - dub test --compiler=${DC} -c library-nonet + - dub build --compiler=${DC} + - DC=${DMD} ./build.sh + - DUB=`pwd`/bin/dub COMPILER=${DC} test/run-unittest.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0e478..fb95c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,12 @@ ### Features and improvements ### - Added support for dub init to take a list of dependencies. (by Colin Grogan) - - Example usage (dub init myProj logger vibe-d gfm --type=vibe.d) - - Dub will then try to get the latest version number for each of these dependencies from code.dlang.org and automatically add them to the dependencies section of dub.json. - - If it cant find the dependant package name, it will ignore it, - - Current functionality is preserved whereby project type can be determined by using [vibe.d, deimos or minimal] after package name. (So example above would be dub init myProj vibe.d logger vibe-d gfm). - - Preferrable to use --type however, as this should be removed for next version. - + - Example usage (dub init myProj logger vibe-d gfm --type=vibe.d) + - Dub will then try to get the latest version number for each of these dependencies from code.dlang.org and automatically add them to the dependencies section of dub.json. + - If it cant find the dependant package name, it will ignore it, + - Current functionality is preserved whereby project type can be determined by using [vibe.d, deimos or minimal] after package name. (So example above would be dub init myProj vibe.d logger vibe-d gfm). + - Preferrable to use --type however, as this should be removed for next version. + v0.9.22 - 2014-09-22 -------------------- diff --git a/README.md b/README.md index 323881f..de6ff58 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,31 @@ -dub package manager -=================== +# dub package manager [![Build Status](https://travis-ci.org/D-Programming-Language/dub.png)](https://travis-ci.org/D-Programming-Language/dub) Package and build manager for [D](http://dlang.org/) applications and libraries. There is a central [package registry](https://github.com/D-Programming-Language/dub-registry/) located at . -[![Build Status](https://travis-ci.org/D-Programming-Language/dub.png)](https://travis-ci.org/D-Programming-Language/dub) - -Introduction ------------- +## Introduction DUB emerged as a more general replacement for [vibe.d's](http://vibed.org/) package manager. It does not imply a dependecy to vibe.d for packages and was extended to not only directly build projects, but also to generate project files (currently [VisualD](https://github.com/rainers/visuald)). [Mono-D](http://mono-d.alexanderbothe.com/) also support the use of dub.json (dub's package description) as project file. The project's philosophy is to keep things as simple as possible. All that is needed to make a project a dub package is to write a short [dub.json](http://code.dlang.org/publish) file and put the source code into a `source` subfolder. It *can* then be registered on the public [package registry](http://code.dlang.org) to be made available for everyone. Any dependencies specified in `dub.json` are automatically downloaded and made available to the project during the build process. - -Key features ------------- +## Key features - Simple package and build description not getting in your way - - Integrated with Git, avoiding maintainance tasks such as incrementing version numbers or uploading new project releases - - Generates VisualD project/solution files, integrated into MonoD - - Support for DMD, GDC and LDC (common DMD flags are translated automatically) - - Supports development workflows by optionally using local directories as a package source - -Future direction ----------------- +## Future direction To make things as flexible as they need to be for certain projects, it is planned to gradually add more options to the [package file format](http://code.dlang.org/package-format) and eventually to add the possibility to specify an external build tool along with the path of it's output files. The idea is that DUB provides a convenient build management that suffices for 99% of projects, but is also usable as a bare package manager that doesn't get in your way if needed. +## Installation -Installation ------------- - -DUB comes [precompiled](http://code.dlang.org/download) for Windows, Mac OS, Linux and FreeBSD. It needs to have libcurl with SSL support installed (except on Windows). +DUB comes [precompiled](http://code.dlang.org/download) for Windows, OS X and Linux. It needs to have libcurl with SSL support installed (except on Windows). The `dub` executable then just needs to be accessible from `PATH` and can be invoked from the root folder of any DUB enabled project to build and run it. @@ -54,12 +40,14 @@ Jordi Sayol maintains a DEB package as part of his [D APT repository](http://d-apt.sourceforge.net). Run `sudo apt-get install dub` to install. -### Mac OS +### OS X Chris Molozian has added DUB to [Homebrew](http://mxcl.github.io/homebrew/). Use `brew install dub` or `brew install dub --HEAD` to install the stable or the git HEAD version respectively. +### Windows -Using DUB as a library ----------------------- +Daniel Jost maintains a dub package on [chocolatey](https://chocolatey.org/packages/dub). Use `cinst dub` or `cinst dub -version #.#.#` to install stable or a custom version respectively. + +## Using DUB as a library The [DUB package of DUB](http://code.dlang.org/packages/dub) can be used as a library to load or manipulate packages, or to resemble any functionality of the command line tool. The former task can be achieved by using the [Package class](https://github.com/D-Programming-Language/dub/blob/master/source/dub/package_.d#L40). For examples on how to replicate the command line functionality, see [commandline.d](https://github.com/D-Programming-Language/dub/blob/master/source/dub/commandline.d). diff --git a/build-files.txt b/build-files.txt index 7c23830..95a467b 100644 --- a/build-files.txt +++ b/build-files.txt @@ -16,7 +16,9 @@ source/dub/compilers/gdc.d source/dub/compilers/ldc.d source/dub/generators/build.d +source/dub/generators/cmake.d source/dub/generators/generator.d +source/dub/generators/sublimetext.d source/dub/generators/visuald.d source/dub/internal/utils.d source/dub/internal/vibecompat/core/file.d @@ -27,4 +29,4 @@ source/dub/internal/vibecompat/inet/url.d source/dub/recipe/json.d source/dub/recipe/packagerecipe.d -source/dub/recipe/sdl.d \ No newline at end of file +source/dub/recipe/sdl.d diff --git a/build.sh b/build.sh index 1c2243f..58bd440 100755 --- a/build.sh +++ b/build.sh @@ -23,7 +23,7 @@ LIBS="-lphobos2 $LIBS" fi elif [ "$DC" = "ldmd2" ]; then - LIBS="-lphobos-ldc $LIBS" + LIBS="-lphobos2-ldc $LIBS" fi # adjust linker flags for dmd command line @@ -37,7 +37,7 @@ echo Running $DC... -$DC -ofbin/dub -g -debug -w -version=DubUseCurl -Isource $* $LIBS @build-files.txt +$DC -ofbin/dub -w -version=DubUseCurl -Isource $* $LIBS @build-files.txt echo DUB has been built as bin/dub. echo echo You may want to run diff --git a/scripts/bash-completion/dub.bash b/scripts/bash-completion/dub.bash index 4af97ba..52315d0 100644 --- a/scripts/bash-completion/dub.bash +++ b/scripts/bash-completion/dub.bash @@ -9,7 +9,7 @@ creation_commands='init run build test generate describe clean dustmite' local management_commands - management_commands='fetch remove upgrade add-path remove-path add-local remove-local list add-override remove-override list-overrides' + management_commands='fetch remove upgrade add-path remove-path add-local remove-local list add-override remove-override list-overrides clean-caches' case "$prev" in -h|--help) diff --git a/scripts/fish-completion/dub.fish b/scripts/fish-completion/dub.fish index 139fda3..1207484 100644 --- a/scripts/fish-completion/dub.fish +++ b/scripts/fish-completion/dub.fish @@ -14,7 +14,7 @@ complete -c dub -n '__fish_use_subcommand' -x -a test -d 'Executes the tests of the selected package' complete -c dub -n '__fish_use_subcommand' -x -a generate -d 'Generates project files using the specified generator' complete -c dub -n '__fish_use_subcommand' -x -a describe -d 'Prints a JSON description of the project and its dependencies' -complete -c dub -n '__fish_use_subcommand' -x -a clean -d 'Removes intermetiate build files and cached build results' +complete -c dub -n '__fish_use_subcommand' -x -a clean -d 'Removes intermediate build files and cached build results' complete -c dub -n '__fish_use_subcommand' -x -a dustmite -d 'Create reduced test cases for build errors' # Package management complete -c dub -n '__fish_use_subcommand' -x -a fetch -d 'Manually retrieves and caches a package' @@ -28,6 +28,7 @@ complete -c dub -n '__fish_use_subcommand' -x -a add-override -d 'Adds a new package override' complete -c dub -n '__fish_use_subcommand' -x -a remove-override -d 'Removes an existing package override' complete -c dub -n '__fish_use_subcommand' -x -a list-overrides -d 'Prints a list of all local package overrides' +complete -c dub -n '__fish_use_subcommand' -x -a clean-caches -d 'Removes cached metadata' # # Subcommand options diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 6571982..108617a 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -20,7 +20,6 @@ import dub.platform : determineCompiler; import dub.project; import dub.internal.utils : getDUBVersion, getClosestMatch; -import dub.version_; import std.algorithm; import std.array; @@ -56,6 +55,17 @@ } args = args[1 .. $]; // strip the application name + // handle direct dub options + if (args.length) switch (args[0]) + { + case "--version": + showVersion(); + return 0; + + default: + break; + } + // parse general options bool verbose, vverbose, quiet, vquiet; bool help, annotate; @@ -357,7 +367,7 @@ string m_buildType; BuildMode m_buildMode; string m_buildConfig; - string m_compilerName = initialCompilerBinary; + string m_compilerName; string m_arch; string[] m_debugVersions; Compiler m_compiler; @@ -368,6 +378,11 @@ bool m_forceRemove = false; } + this() + { + m_compilerName = defaultCompiler(); + } + override void prepare(scope CommandArgs args) { args.getopt("b|build", &m_buildType, [ @@ -382,7 +397,7 @@ "Specifies the compiler binary to use (can be a path).", "Arbitrary pre- and suffixes to the identifiers below are recognized (e.g. ldc2 or dmd-2.063) and matched to the proper compiler type:", " "~["dmd", "gdc", "ldc", "gdmd", "ldmd"].join(", "), - "Default value: "~initialCompilerBinary, + "Default value: "~m_compilerName, ]); args.getopt("a|arch", &m_arch, [ "Force a different architecture (e.g. x86 or x86_64)" @@ -505,6 +520,8 @@ "Generates project files using one of the supported generators:", "", "visuald - VisualD project files", + "sublimetext - SublimeText project file", + "cmake - CMake build scripts", "build - Builds the package directly", "", "An optional package name can be given to generate a different package than the root/CWD package." @@ -1451,7 +1468,7 @@ private void showHelp(in CommandGroup[] commands, CommandArgs common_args) { writeln( -`USAGE: dub [] [] [-- []] +`USAGE: dub [--version] [] [] [-- []] Manages the DUB project in the current directory. If the command is omitted, DUB will default to "run". When running an application, "--" can be used to @@ -1494,6 +1511,11 @@ writeln(); writeOptions(common_args); writeln(); + showVersion(); +} + +private void showVersion() +{ writefln("DUB version %s, built on %s", getDUBVersion(), __DATE__); } diff --git a/source/dub/compilers/buildsettings.d b/source/dub/compilers/buildsettings.d index f64362f..52089b5 100644 --- a/source/dub/compilers/buildsettings.d +++ b/source/dub/compilers/buildsettings.d @@ -185,7 +185,8 @@ library, sourceLibrary, dynamicLibrary, - staticLibrary + staticLibrary, + object } enum BuildRequirements { diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index dafa331..2acc298 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -12,6 +12,7 @@ import dub.compilers.dmd; import dub.compilers.gdc; import dub.compilers.ldc; +import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; @@ -30,7 +31,6 @@ registerCompiler(new LdcCompiler); } - Compiler getCompiler(string name) { foreach (c; s_compilers) @@ -45,6 +45,33 @@ throw new Exception("Unknown compiler: "~name); } +string defaultCompiler() +{ + static string name; + if (!name.length) name = findCompiler(); + return name; +} + +private string findCompiler() +{ + import std.process : env=environment; + import dub.version_ : initialCompilerBinary; + version (Windows) enum sep = ";", exe = ".exe"; + version (Posix) enum sep = ":", exe = ""; + + auto def = Path(initialCompilerBinary); + if (def.absolute && existsFile(def)) + return initialCompilerBinary; + + auto compilers = ["dmd", "gdc", "gdmd", "ldc2", "ldmd2"]; + if (!def.absolute) + compilers = initialCompilerBinary ~ compilers; + + auto paths = env.get("PATH", "").splitter(sep).map!Path; + auto res = compilers.find!(bin => paths.canFind!(p => existsFile(p ~ (bin~exe)))); + return res.empty ? initialCompilerBinary : res.front; +} + void registerCompiler(Compiler c) { s_compilers ~= c; @@ -339,6 +366,10 @@ if( platform.platform.canFind("windows") ) return settings.targetName ~ ".dll"; else return "lib" ~ settings.targetName ~ ".so"; + case TargetType.object: + if (platform.platform.canFind("windows")) + return settings.targetName ~ ".obj"; + else return settings.targetName ~ ".o"; } } @@ -366,15 +397,16 @@ import dub.internal.vibecompat.data.json; import dub.internal.utils; - auto path = getTempDir() ~ "dub_platform_probe.d"; + auto path = getTempFile("dub_platform_probe", ".d"); auto fil = openFile(path, FileMode.CreateTrunc); scope (failure) { fil.close(); - removeFile(path); } fil.write(q{ + module dub_platform_probe; + template toString(int v) { enum toString = v.stringof; } pragma(msg, `{`); @@ -388,7 +420,7 @@ pragma(msg, ` ` ~ determineArchitecture()); pragma(msg, ` ],`); pragma(msg, `}`); - + string determinePlatform() { string ret; diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index 9f5e65d..599f809 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -169,6 +169,9 @@ version (Windows) settings.addDFlags("-shared"); else settings.addDFlags("-shared", "-fPIC"); break; + case TargetType.object: + settings.addDFlags("-c"); + break; } if (tpath is null) @@ -178,9 +181,8 @@ void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) { - auto res_file = getTempDir() ~ ("dub-build-"~uniform(0, uint.max).to!string~"-.rsp"); + auto res_file = getTempFile("dub-build", ".rsp"); std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => s.canFind(' ') ? "\""~s~"\"" : s), "\n")); - scope (exit) remove(res_file.toNativeString()); logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); @@ -190,14 +192,18 @@ { import std.string; auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); - auto args = [platform.compilerBinary, "-of"~tpath.toNativeString()]; + auto args = ["-of"~tpath.toNativeString()]; args ~= objects; args ~= settings.sourceFiles; version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being speficied in the wrong order by DMD args ~= settings.lflags.map!(l => "-L"~l)().array; args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; - logDiagnostic("%s", args.join(" ")); - invokeTool(args, output_callback); + + auto res_file = getTempFile("dub-build", ".lnk"); + std.file.write(res_file.toNativeString(), join(args, "\n")); + + logDiagnostic("%s %s", platform.compilerBinary, args.join(" ")); + invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); } private static bool isLinkerDFlag(string arg) diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index e31053f..a82339c 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -67,7 +67,12 @@ } settings.addDFlags(arch_flags); - auto result = executeShell(escapeShellCommand(compiler_binary ~ arch_flags ~ ["-c", "-o", (getTempDir()~"dub_platform_probe").toNativeString(), fil.toNativeString()])); + auto binary_file = getTempFile("dub_platform_probe"); + auto result = executeShell(escapeShellCommand( + compiler_binary ~ + arch_flags ~ + ["-c", "-o", binary_file.toNativeString(), fil.toNativeString()] + )); enforce(result.status == 0, format("Failed to invoke the compiler %s to determine the build platform: %s", compiler_binary, result.output)); @@ -162,6 +167,7 @@ case TargetType.executable: break; case TargetType.library: case TargetType.staticLibrary: + case TargetType.object: settings.addDFlags("-c"); break; case TargetType.dynamicLibrary: @@ -176,9 +182,8 @@ void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) { - auto res_file = getTempDir() ~ ("dub-build-"~uniform(0, uint.max).to!string~"-.rsp"); + auto res_file = getTempFile("dub-build", ".rsp"); std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => escape(s)), "\n")); - scope (exit) remove(res_file.toNativeString()); logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index f75caa8..32a4520 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -157,6 +157,9 @@ case TargetType.dynamicLibrary: settings.addDFlags("-shared"); break; + case TargetType.object: + settings.addDFlags("-c"); + break; } if (tpath is null) @@ -166,9 +169,8 @@ void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) { - auto res_file = getTempDir() ~ ("dub-build-"~uniform(0, uint.max).to!string~"-.rsp"); + auto res_file = getTempFile("dub-build", ".rsp"); std.file.write(res_file.toNativeString(), join(cast(string[])settings.dflags, "\n")); - scope (exit) remove(res_file.toNativeString()); logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); diff --git a/source/dub/dependency.d b/source/dub/dependency.d index c02a5d8..a00a6da 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -133,7 +133,7 @@ @property bool isExactVersion() const { return m_versA == m_versB; } @property Version version_() const { - enforce(m_versA == m_versB, "Dependency "~versionString()~" is no exact version."); + enforce(m_versA == m_versB, "Dependency "~versionString~" is no exact version."); return m_versA; } @@ -176,10 +176,10 @@ Json toJson() const { Json json; if( path.empty && !optional ){ - json = Json(versionString()); + json = Json(this.versionString); } else { json = Json.emptyObject; - json["version"] = versionString(); + json["version"] = this.versionString; if (!path.empty) json["path"] = path.toString(); if (optional) json["optional"] = true; } @@ -202,7 +202,7 @@ logDiagnostic("Ignoring version specification (%s) for path based dependency %s", pv.get!string, pp.get!string); dep = Dependency.ANY; - dep.path = Path(verspec.path.get!string()); + dep.path = Path(verspec.path.get!string); } else { enforce("version" in verspec, "No version field specified!"); auto ver = verspec["version"].get!string; @@ -210,11 +210,11 @@ dep = Dependency(ver); } if( auto po = "optional" in verspec ) { - dep.optional = verspec.optional.get!bool(); + dep.optional = verspec.optional.get!bool; } } else { // canonical "package-id": "version" - dep = Dependency(verspec.get!string()); + dep = Dependency(verspec.get!string); } return dep; } @@ -347,28 +347,28 @@ a = Dependency("<=1.0.0 >=2.0.0"); assert (!a.valid(), a.toString()); - a = Dependency(">=1.0.0 <=5.0.0"), b = Dependency(">=2.0.0"); + a = Dependency(">=1.0.0 <=5.0.0"); b = Dependency(">=2.0.0"); assert (a.merge(b).valid() && a.merge(b).versionString == ">=2.0.0 <=5.0.0", a.merge(b).toString()); assertThrown(a = Dependency(">1.0.0 ==5.0.0"), "Construction is invalid"); - a = Dependency(">1.0.0"), b = Dependency("<2.0.0"); + a = Dependency(">1.0.0"); b = Dependency("<2.0.0"); assert (a.merge(b).valid(), a.merge(b).toString()); assert (a.merge(b).versionString == ">1.0.0 <2.0.0", a.merge(b).toString()); - a = Dependency(">2.0.0"), b = Dependency("<1.0.0"); + a = Dependency(">2.0.0"); b = Dependency("<1.0.0"); assert (!(a.merge(b)).valid(), a.merge(b).toString()); - a = Dependency(">=2.0.0"), b = Dependency("<=1.0.0"); + a = Dependency(">=2.0.0"); b = Dependency("<=1.0.0"); assert (!(a.merge(b)).valid(), a.merge(b).toString()); - a = Dependency("==2.0.0"), b = Dependency("==1.0.0"); + a = Dependency("==2.0.0"); b = Dependency("==1.0.0"); assert (!(a.merge(b)).valid(), a.merge(b).toString()); - a = Dependency("1.0.0"), b = Dependency("==1.0.0"); + a = Dependency("1.0.0"); b = Dependency("==1.0.0"); assert (a == b); - a = Dependency("<=2.0.0"), b = Dependency("==1.0.0"); + a = Dependency("<=2.0.0"); b = Dependency("==1.0.0"); Dependency m = a.merge(b); assert (m.valid(), m.toString()); assert (m.matches(Version("1.0.0"))); diff --git a/source/dub/dub.d b/source/dub/dub.d index 4d46021..ad31b55 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -362,7 +362,7 @@ } // generate main file - Path mainfile = getTempDir() ~ "dub_test_root.d"; + Path mainfile = getTempFile("dub_test_root", ".d"); tcinfo.sourceFiles[""] ~= mainfile.toNativeString(); tcinfo.mainSourceFile = mainfile.toNativeString(); if (!m_dryRun) { @@ -436,7 +436,7 @@ /// Returns all cached packages as a "packageId" = "version" associative array - string[string] cachedPackages() const { return m_project.cachedPackagesIDs(); } + string[string] cachedPackages() const { return m_project.cachedPackagesIDs; } /// Fetches the package matching the dependency and places it in the specified location. Package fetch(string packageId, const Dependency dep, PlacementLocation location, FetchOptions options, string reason = "") @@ -449,7 +449,7 @@ supplier = ps; break; } catch(Exception e) { - logDiagnostic("Package %s not found for %s: %s", packageId, ps.description(), e.msg); + logDiagnostic("Package %s not found for %s: %s", packageId, ps.description, e.msg); logDebug("Full error: %s", e.toString().sanitize()); } } @@ -605,32 +605,29 @@ void createEmptyPackage(Path path, string[] deps, string type) { - if( !path.absolute() ) path = m_rootPath ~ path; + if (!path.absolute) path = m_rootPath ~ path; path.normalize(); if (m_dryRun) return; string[string] depVers; + string[] notFound; // keep track of any failed packages in here foreach(ps; this.m_packageSuppliers){ foreach(dep; deps){ try{ auto versionStrings = ps.getVersions(dep); depVers[dep] = versionStrings[$-1].toString; } catch(Exception e){ - auto packages = ps.getPackageNames(); - string[][size_t] lds; //holds the levenshteinDistance from dep for each package - foreach(pack; packages){ - lds[dep.levenshteinDistance(pack)] ~= pack; - } - auto closestKey = lds.keys.sort.front; - if(closestKey <= 4){ - logError("Error, no package \"%s\" found. Did you mean %s?", dep, lds[closestKey]); - } else{ - logError("Error, no package \"%s\" found. Exiting...", dep); - } - return; + notFound ~= dep; } } } + if(notFound.length > 1){ + throw new Exception(format("Couldn't find packages: %-(%s, %).", notFound)); + } + else if(notFound.length == 1){ + throw new Exception(format("Couldn't find package: %-(%s, %).", notFound)); + } + initPackage(path, depVers, type); //Act smug to the user. @@ -681,7 +678,7 @@ if (!run) { commands ~= dub_path~"ddox generate-html --navigation-type=ModuleTree docs.json docs"; version(Windows) commands ~= "xcopy /S /D "~dub_path~"public\\* docs\\"; - else commands ~= "cp -ru \""~dub_path~"public\"/* docs/"; + else commands ~= "rsync -ru '"~dub_path~"public/' docs/"; } runCommands(commands); diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index e7b057a..685b34c 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -306,7 +306,7 @@ logDiagnostic("Copying target from %s to %s", src.toNativeString(), buildsettings.targetPath); if (!existsFile(Path(buildsettings.targetPath))) mkdirRecurse(buildsettings.targetPath); - symlinkFile(src, Path(buildsettings.targetPath) ~ filename, true); + hardLinkFile(src, Path(buildsettings.targetPath) ~ filename, true); } private bool isUpToDate(Path target_path, BuildSettings buildsettings, BuildPlatform platform, in Package main_pack, in Package[] packages, in Path[] additional_dep_files) @@ -350,15 +350,20 @@ /// Output an unique name to represent the source file. /// Calls with path that resolve to the same file on the filesystem will return the same, /// unless they include different symbolic links (which are not resolved). - static string pathToObjName(string path) { return std.path.buildNormalizedPath(getcwd(), path~objSuffix)[1..$].replace("/", "."); } + + static string pathToObjName(string path) + { + return std.path.stripDrive(std.path.buildNormalizedPath(getcwd(), path~objSuffix))[1..$].replace(std.path.dirSeparator, "."); + } + /// Compile a single source file (srcFile), and write the object to objName. static string compileUnit(string srcFile, string objName, BuildSettings bs, GeneratorSettings gs) { Path tempobj = Path(bs.targetPath)~objName; string objPath = tempobj.toNativeString(); bs.libs = null; bs.lflags = null; - bs.addDFlags("-c"); bs.sourceFiles = [ srcFile ]; + bs.targetType = TargetType.object; gs.compiler.prepareBuildSettings(bs, BuildSetting.commandLine); gs.compiler.setTarget(bs, gs.platform, objPath); gs.compiler.invoke(bs, gs.platform, gs.compileCallback); @@ -378,19 +383,22 @@ removeFile(tpath); } if (settings.buildMode == BuildMode.singleFile && generate_binary) { + import std.parallelism, std.range : walkLength; + auto lbuildsettings = buildsettings; - auto objs = appender!(string[])(); + auto srcs = buildsettings.sourceFiles.filter!(f => !isLinkerFile(f)); + auto objs = new string[](srcs.walkLength); logInfo("Compiling using %s...", settings.platform.compilerBinary); - foreach (file; buildsettings.sourceFiles.filter!(f=>!isLinkerFile(f))) { - logInfo("Compiling %s...", file); - objs.put(compileUnit(file, pathToObjName(file), buildsettings, settings)); + foreach (i, src; srcs.parallel(1)) { + logInfo("Compiling %s...", src); + objs[i] = compileUnit(src, pathToObjName(src), buildsettings, settings); } logInfo("Linking..."); lbuildsettings.sourceFiles = is_static_library ? [] : lbuildsettings.sourceFiles.filter!(f=> f.isLinkerFile()).array; settings.compiler.setTarget(lbuildsettings, settings.platform); settings.compiler.prepareBuildSettings(lbuildsettings, BuildSetting.commandLineSeparate|BuildSetting.sourceFiles); - settings.compiler.invokeLinker(lbuildsettings, settings.platform, objs.data, settings.linkCallback); + settings.compiler.invokeLinker(lbuildsettings, settings.platform, objs, settings.linkCallback); /* NOTE: for DMD experimental separate compile/link is used, but this is not yet implemented diff --git a/source/dub/generators/cmake.d b/source/dub/generators/cmake.d new file mode 100644 index 0000000..eb51be7 --- /dev/null +++ b/source/dub/generators/cmake.d @@ -0,0 +1,142 @@ +/** + Generator for CMake build scripts + + Copyright: © 2015 Steven Dwy + License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. + Authors: Steven Dwy +*/ +module dub.generators.cmake; + +import dub.compilers.buildsettings; +import dub.generators.generator; +import dub.internal.vibecompat.core.log; +import dub.internal.vibecompat.core.file; +import dub.internal.vibecompat.inet.path; +import dub.project; + +import std.algorithm: map, uniq; +import std.algorithm : stdsort = sort; // to avoid clashing with built-in sort +import std.array: appender, join, replace; +import std.stdio: File, write; +import std.string: format; + +class CMakeGenerator: ProjectGenerator +{ + this(Project project) + { + super(project); + } + + override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) + { + auto script = appender!(char[]); + auto scripts = appender!(string[]); + bool[string] visited; + Path projectRoot = m_project.rootPackage.path; + Path cmakeListsPath = projectRoot ~ "CMakeLists.txt"; + + foreach(name, info; targets) + { + if(visited.get(name, false)) + continue; + + visited[name] = true; + name = name.sanitize; + string targetType; + string libType; + bool addTarget = true; + + switch(info.buildSettings.targetType) with(TargetType) + { + case autodetect: + throw new Exception("Don't know what to do about autodetect target type"); + case executable: + targetType = "executable"; + + break; + case dynamicLibrary: + libType = "SHARED"; + + goto case; + case library: + case staticLibrary: + targetType = "library"; + + break; + case sourceLibrary: + addTarget = false; + + break; + case none: + continue; + default: + assert(false); + } + + script.put("include(UseD)\n"); + script.put( + "add_d_conditions(VERSION %s DEBUG %s)\n".format( + info.buildSettings.versions.dup.join(" "), + info.buildSettings.debugVersions.dup.join(" "), + ) + ); + + foreach(directory; info.buildSettings.importPaths) + script.put("include_directories(%s)\n".format(directory)); + + if(addTarget) + { + script.put("add_%s(%s %s\n".format(targetType, name, libType)); + + foreach(file; info.buildSettings.sourceFiles) + script.put(" %s\n".format(file)); + + script.put(")\n"); + script.put( + "target_link_libraries(%s %s %s)\n".format( + name, + (info.dependencies ~ info.linkDependencies).dup.stdsort.uniq.map!sanitize.join(" "), + info.buildSettings.libs.dup.join(" ") + ) + ); + script.put( + `set_target_properties(%s PROPERTIES TEXT_INCLUDE_DIRECTORIES "%s")`.format( + name, + info.buildSettings.stringImportPaths.dup.join(";") + ) ~ "\n" + ); + } + + string filename = (projectRoot ~ "%s.cmake".format(name)).toNativeString; + File file = File(filename, "w"); + + file.write(script.data); + file.close; + script.shrinkTo(0); + scripts.put(filename); + } + + if(!cmakeListsPath.existsFile) + { + logWarn("You must use a fork of CMake which has D support for these scripts to function properly."); + logWarn("It is available at https://github.com/trentforkert/cmake"); + logInfo("Generating default CMakeLists.txt"); + script.put("cmake_minimum_required(VERSION 3.0)\n"); + script.put("project(%s D)\n".format(m_project.rootPackage.name)); + + foreach(path; scripts.data) + script.put("include(%s)\n".format(path)); + + File file = File(cmakeListsPath.toNativeString, "w"); + + file.write(script.data); + file.close; + } + } +} + +///Transform a package name into a valid CMake target name. +string sanitize(string name) +{ + return name.replace(":", "_"); +} diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 533a1c4..d552e80 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -8,7 +8,9 @@ module dub.generators.generator; import dub.compilers.compiler; +import dub.generators.cmake; import dub.generators.build; +import dub.generators.sublimetext; import dub.generators.visuald; import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.core.log; @@ -200,6 +202,9 @@ buildsettings.add(depbs); + if (depbs.targetType == TargetType.executable) + continue; + auto pt = (generates_binary ? pack.name : bin_pack) in targets; assert(pt !is null); if (auto pdt = depname in targets) { @@ -302,6 +307,12 @@ case "visuald": logDebug("Creating VisualD generator."); return new VisualDGenerator(project); + case "sublimetext": + logDebug("Creating SublimeText generator."); + return new SublimeTextGenerator(project); + case "cmake": + logDebug("Creating CMake generator."); + return new CMakeGenerator(project); } } @@ -332,6 +343,36 @@ mkdirRecurse(buildsettings.targetPath); if (buildsettings.copyFiles.length) { + void copyFolderRec(Path folder, Path dstfolder) + { + mkdirRecurse(dstfolder.toNativeString()); + foreach (de; iterateDirectory(folder.toNativeString())) { + if (de.isDirectory) { + copyFolderRec(folder ~ de.name, dstfolder ~ de.name); + } else { + try hardLinkFile(folder ~ de.name, dstfolder ~ de.name, true); + catch (Exception e) { + logWarn("Failed to copy file %s: %s", (folder ~ de.name).toNativeString(), e.msg); + } + } + } + } + + void tryCopyDir(string file) + { + auto src = Path(file); + if (!src.absolute) src = pack_path ~ src; + auto dst = target_path ~ Path(file).head; + if (src == dst) { + logDiagnostic("Skipping copy of %s (same source and destination)", file); + return; + } + logDiagnostic(" %s to %s", src.toNativeString(), dst.toNativeString()); + try { + copyFolderRec(src, dst); + } catch(Exception e) logWarn("Failed to copy %s to %s: %s", src.toNativeString(), dst.toNativeString(), e.msg); + } + void tryCopyFile(string file) { auto src = Path(file); @@ -343,7 +384,7 @@ } logDiagnostic(" %s to %s", src.toNativeString(), dst.toNativeString()); try { - copyFile(src, dst, true); + hardLinkFile(src, dst, true); } catch(Exception e) logWarn("Failed to copy %s to %s: %s", src.toNativeString(), dst.toNativeString(), e.msg); } logInfo("Copying files for %s...", pack); @@ -358,7 +399,10 @@ } else { - tryCopyFile(f); + if (f.isDir) + tryCopyDir(f); + else + tryCopyFile(f); } } if (globs.length) // Search all files for glob matches @@ -369,13 +413,17 @@ { if (f.globMatch(glob)) { - tryCopyFile(f); + if (f.isDir) + tryCopyDir(f); + else + tryCopyFile(f); break; } } } } } + } } diff --git a/source/dub/generators/sublimetext.d b/source/dub/generators/sublimetext.d new file mode 100644 index 0000000..1a7db6a --- /dev/null +++ b/source/dub/generators/sublimetext.d @@ -0,0 +1,114 @@ +/** +Generator for SublimeText project files + +Copyright: © 2014 Nicholas Londey +License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. +Authors: Nicholas Londey +*/ +module dub.generators.sublimetext; + +import dub.compilers.compiler; +import dub.generators.generator; +import dub.internal.vibecompat.core.log; +import dub.internal.vibecompat.data.json; +import dub.packagemanager; +import dub.project; + +import std.algorithm; +import std.array; +import std.compiler; +import std.file; +import std.path; +import std.range; +import std.string; + + +class SublimeTextGenerator : ProjectGenerator { + + this(Project project) + { + super(project); + } + + override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets) + { + auto buildSettings = targets[m_project.name].buildSettings; + logDebug("About to generate sublime project for %s.", m_project.rootPackage.name); + + auto root = Json([ + "folders": targets.byValue.map!targetFolderJson.array.Json, + "build_systems": buildSystems(settings.platform), + ]); + + auto jsonString = appender!string(); + writePrettyJsonString(jsonString, root); + + write(m_project.name ~ ".sublime-project", jsonString.data); + + logInfo("SublimeText project generated."); + } +} + + +Json targetFolderJson(in ProjectGenerator.TargetInfo target) +{ + return [ + "name": target.pack.name.Json, + "path": target.pack.path.toNativeString.Json, + "follow_symlinks": true.Json, + "folder_exclude_patterns": [".dub"].map!Json.array.Json, + ].Json; +} + + +Json buildSystems(BuildPlatform buildPlatform, string workingDiretory = getcwd()) +{ + enum BUILD_TYPES = [ + //"plain", + "debug", + "release", + //"unittest", + "docs", + "ddox", + "profile", + "cov", + "unittest-cov", + ]; + + auto arch = buildPlatform.architecture[0]; + + Json makeBuildSystem(string buildType) + { + return Json([ + "name": "DUB build " ~ buildType.Json, + "cmd": ["dub", "build", "--build=" ~ buildType, "--arch=" ~ arch].map!Json.array.Json, + "file_regex": r"^(.+)\(([0-9]+)\)\:() (.*)$".Json, + "working_dir": workingDiretory.Json, + "variants": [ + [ + "name": "Run".Json, + "cmd": ["dub", "run", "--build=" ~ buildType, "--arch=" ~ arch].map!Json.array.Json, + ].Json + ].array.Json, + ]); + } + + auto buildSystems = BUILD_TYPES.map!makeBuildSystem.array; + + buildSystems ~= [ + "name": "DUB test".Json, + "cmd": ["dub", "test", "--arch=" ~ arch].map!Json.array.Json, + "file_regex": r"^(.+)\(([0-9]+)\)\:() (.*)$".Json, + "working_dir": workingDiretory.Json, + ].Json; + + return buildSystems.array.Json; +} + +unittest +{ + auto buildPlatform = BuildPlatform(); + buildPlatform.architecture ~= "x86_64"; + + auto result = buildPlatform.buildSystems.toString; +} diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index 5a6a216..2608f15 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -96,12 +96,16 @@ // Global section contains configurations ret.put("Global\n"); ret.put("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"); - ret.formattedWrite("\t\t%s|Win32 = %s|Win32\n", settings.buildType, settings.buildType); + ret.formattedWrite("\t\t%s|%s = %s|%s\n", + settings.buildType, + settings.platform.architecture[0].vsArchitecture, + settings.buildType, + settings.platform.architecture[0].vsArchitecture); ret.put("\tEndGlobalSection\n"); ret.put("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"); const string[] sub = ["ActiveCfg", "Build.0"]; - const string[] conf = [settings.buildType~"|Win32"]; + const string[] conf = [settings.buildType~"|"~settings.platform.architecture[0].vsArchitecture]; auto projectUuid = guid(mainpack); foreach (t; targets.byKey) foreach (c; conf) @@ -117,10 +121,10 @@ ret.put("EndGlobal\n"); // Writing solution file - logDebug("About to write to .sln file with %s bytes", to!string(ret.data().length)); + logDebug("About to write to .sln file with %s bytes", to!string(ret.data.length)); auto sln = openFile(solutionFileName(), FileMode.CreateTrunc); scope(exit) sln.close(); - sln.put(ret.data()); + sln.put(ret.data); sln.flush(); } @@ -223,10 +227,10 @@ ret.put("\n "); ret.put("\n \n"); - logDebug("About to write to '%s.visualdproj' file %s bytes", getPackageFileName(packname), ret.data().length); + logDebug("About to write to '%s.visualdproj' file %s bytes", getPackageFileName(packname), ret.data.length); auto proj = openFile(projFileName(packname), FileMode.CreateTrunc); scope(exit) proj.close(); - proj.put(ret.data()); + proj.put(ret.data); proj.flush(); } @@ -250,12 +254,7 @@ } foreach(architecture; settings.platform.architecture) { - string arch; - switch(architecture) { - default: logWarn("Unsupported platform('%s'), defaulting to x86", architecture); goto case; - case "x86": arch = "Win32"; break; - case "x86_64": arch = "x64"; break; - } + auto arch = architecture.vsArchitecture; ret.formattedWrite(" \n", to!string(type), arch); // FIXME: handle compiler options in an abstract way instead of searching for DMD specific flags @@ -455,8 +454,8 @@ int opCmp(ref const SourceFile rhs) const { return sortOrder(this, rhs); } // "a < b" for folder structures (deepest folder first, else lexical) private final static int sortOrder(ref const SourceFile a, ref const SourceFile b) { - assert(!a.structurePath.empty()); - assert(!b.structurePath.empty()); + assert(!a.structurePath.empty); + assert(!b.structurePath.empty); auto as = a.structurePath; auto bs = b.structurePath; @@ -517,3 +516,12 @@ { return pack.replace(":", "_"); } + +private @property string vsArchitecture(string architecture) +{ + switch(architecture) { + default: logWarn("Unsupported platform('%s'), defaulting to x86", architecture); goto case; + case "x86": return "Win32"; + case "x86_64": return "x64"; + } +} diff --git a/source/dub/init.d b/source/dub/init.d index 1e9bd83..40a5cd7 100644 --- a/source/dub/init.d +++ b/source/dub/init.d @@ -111,7 +111,7 @@ version (Windows) username = environment.get("USERNAME", "Peter Parker"); else username = environment.get("USER", "Peter Parker"); - auto fil = openFile(root_path ~ defaultPackageFilename(), FileMode.Append); + auto fil = openFile(root_path ~ defaultPackageFilename, FileMode.Append); scope(exit) fil.close(); fil.formattedWrite("{\n\t\"name\": \"%s\",\n", root_path.head.toString().toLower()); diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index e71be4e..de7ebc6 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -28,13 +28,26 @@ Path getTempDir() { - auto tmp = environment.get("TEMP"); - if( !tmp.length ) tmp = environment.get("TMP"); - if( !tmp.length ){ - version(Posix) tmp = "/tmp/"; - else tmp = "./"; + return Path(std.file.tempDir()); +} + +private Path[] temporary_files; + +Path getTempFile(string prefix, string extension = null) +{ + import std.uuid : randomUUID; + + auto path = getTempDir() ~ (prefix ~ "-" ~ randomUUID.toString() ~ extension); + temporary_files ~= path; + return path; +} + +static ~this() +{ + foreach (path; temporary_files) + { + std.file.remove(path.toNativeString()); } - return Path(tmp); } bool isEmptyDir(Path p) { @@ -225,3 +238,18 @@ auto idx = distMap.countUntil!(a => a <= distance); return (idx == -1) ? null : array[idx]; } + +/** + Searches for close matches to input in range. R must be a range of strings + Note: Sorts the strings range. Use std.range.indexed to avoid this... + */ +auto fuzzySearch(R)(R strings, string input){ + import std.algorithm : levenshteinDistance, schwartzSort, partition3; + import std.traits : isSomeString; + import std.range : ElementType; + + static assert(isSomeString!(ElementType!R), "Cannot call fuzzy search on non string rang"); + immutable threshold = input.length / 4; + return strings.partition3!((a, b) => a.length + threshold < b.length)(input)[1] + .schwartzSort!(p => levenshteinDistance(input.toUpper, p.toUpper)); +} diff --git a/source/dub/internal/vibecompat/core/file.d b/source/dub/internal/vibecompat/core/file.d index 0e004dd..1ca6a25 100644 --- a/source/dub/internal/vibecompat/core/file.d +++ b/source/dub/internal/vibecompat/core/file.d @@ -64,7 +64,7 @@ case FileMode.Append: fmode = std.stream.FileMode.Append; break; } auto ret = new std.stream.File(path.toNativeString(), fmode); - assert(ret.isOpen()); + assert(ret.isOpen); return RangeFile(ret); } /// ditto @@ -135,29 +135,43 @@ copyFile(Path(from), Path(to)); } -/** - Creates a symlink. -*/ -version (Windows) - alias symlinkFile = copyFile; // TODO: symlinks on Windows -else version (Posix) -{ - void symlinkFile(Path from, Path to, bool overwrite = false) - { - if (existsFile(to)) { - enforce(overwrite, "Destination file already exists."); - // remove file before copy to allow "overwriting" files that are in - // use on Linux - removeFile(to); - } +version (Windows) extern(Windows) int CreateHardLinkW(in wchar* to, in wchar* from, void* attr=null); - .symlink(from.toNativeString(), to.toNativeString()); - } +// guess whether 2 files are identical, ignores filename and content +private bool sameFile(Path a, Path b) +{ + static assert(__traits(allMembers, FileInfo)[0] == "name"); + return getFileInfo(a).tupleof[1 .. $] == getFileInfo(b).tupleof[1 .. $]; } -void symlinkFile(string from, string to) +/** + Creates a hardlink. +*/ +void hardLinkFile(Path from, Path to, bool overwrite = false) { - symlinkFile(Path(from), Path(to)); + if (existsFile(to)) { + enforce(overwrite, "Destination file already exists."); + if (auto fe = collectException!FileException(removeFile(to))) { + version (Windows) if (sameFile(from, to)) return; + throw fe; + } + } + + version (Windows) + { + alias cstr = toUTFz!(const(wchar)*); + if (CreateHardLinkW(cstr(to.toNativeString), cstr(from.toNativeString))) + return; + } + else + { + import core.sys.posix.unistd : link; + alias cstr = toUTFz!(const(char)*); + if (!link(cstr(from.toNativeString), cstr(to.toNativeString))) + return; + } + // fallback to copy + copyFile(from, to, overwrite); } /** diff --git a/source/dub/internal/vibecompat/core/log.d b/source/dub/internal/vibecompat/core/log.d index 9bce901..5d0c705 100644 --- a/source/dub/internal/vibecompat/core/log.d +++ b/source/dub/internal/vibecompat/core/log.d @@ -73,10 +73,10 @@ if( level >= s_minLevel ){ if (level == LogLevel.info) { - stdout.writeln(txt.data()); + stdout.writeln(txt.data); stdout.flush(); } else { - stderr.writeln(txt.data()); + stderr.writeln(txt.data); stderr.flush(); } } diff --git a/source/dub/package_.d b/source/dub/package_.d index 9c23902..0f3d895 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -142,58 +142,7 @@ // use the given recipe as the basis m_info = recipe; - // WARNING: changed semantics here. Previously, "sourcePaths" etc. - // could overwrite what was determined here. Now the default paths - // are always added. This must be fixed somehow! - - // check for default string import folders - foreach(defvf; ["views"]){ - auto p = m_path ~ defvf; - if( existsFile(p) ) - m_info.buildSettings.stringImportPaths[""] ~= defvf; - } - - // check for default source folders - string app_main_file; - auto pkg_name = recipe.name.length ? recipe.name : "unknown"; - foreach(defsf; ["source/", "src/"]){ - auto p = m_path ~ defsf; - if( existsFile(p) ){ - m_info.buildSettings.sourcePaths[""] ~= defsf; - m_info.buildSettings.importPaths[""] ~= defsf; - foreach (fil; ["app.d", "main.d", pkg_name ~ "/main.d", pkg_name ~ "/" ~ "app.d"]) - if (existsFile(p ~ fil)) { - app_main_file = Path(defsf ~ fil).toNativeString(); - break; - } - } - } - - // generate default configurations if none are defined - if (m_info.configurations.length == 0) { - if (m_info.buildSettings.targetType == TargetType.executable) { - BuildSettingsTemplate app_settings; - app_settings.targetType = TargetType.executable; - if (m_info.buildSettings.mainSourceFile.empty) app_settings.mainSourceFile = app_main_file; - m_info.configurations ~= ConfigurationInfo("application", app_settings); - } else if (m_info.buildSettings.targetType != TargetType.none) { - BuildSettingsTemplate lib_settings; - lib_settings.targetType = m_info.buildSettings.targetType == TargetType.autodetect ? TargetType.library : m_info.buildSettings.targetType; - - if (m_info.buildSettings.targetType == TargetType.autodetect) { - if (app_main_file.length) { - lib_settings.excludedSourceFiles[""] ~= app_main_file; - - BuildSettingsTemplate app_settings; - app_settings.targetType = TargetType.executable; - app_settings.mainSourceFile = app_main_file; - m_info.configurations ~= ConfigurationInfo("application", app_settings); - } - } - - m_info.configurations ~= ConfigurationInfo("library", lib_settings); - } - } + fillWithDefaults(); simpleLint(); } @@ -240,7 +189,7 @@ void storeInfo() { enforce(!ver.isUnknown, "Trying to store a package with an 'unknown' version, this is not supported."); - auto filename = m_path ~ defaultPackageFilename(); + auto filename = m_path ~ defaultPackageFilename; auto dstFile = openFile(filename.toNativeString(), FileMode.CreateTrunc); scope(exit) dstFile.close(); dstFile.writePrettyJsonString(m_info.toJson()); @@ -460,6 +409,71 @@ dst.files = Json(files); } + private void fillWithDefaults() + { + auto bs = &m_info.buildSettings; + + // check for default string import folders + if ("" !in bs.stringImportPaths) { + foreach(defvf; ["views"]){ + if( existsFile(m_path ~ defvf) ) + bs.stringImportPaths[""] ~= defvf; + } + } + + // check for default source folders + immutable hasSP = ("" in bs.sourcePaths) !is null; + immutable hasIP = ("" in bs.importPaths) !is null; + if (!hasSP || !hasIP) { + foreach (defsf; ["source/", "src/"]) { + if (existsFile(m_path ~ defsf)) { + if (!hasSP) bs.sourcePaths[""] ~= defsf; + if (!hasIP) bs.importPaths[""] ~= defsf; + } + } + } + + // check for default app_main + string app_main_file; + auto pkg_name = m_info.name.length ? m_info.name : "unknown"; + foreach(sf; bs.sourcePaths.get("", null)){ + auto p = m_path ~ sf; + if( !existsFile(p) ) continue; + foreach(fil; ["app.d", "main.d", pkg_name ~ "/main.d", pkg_name ~ "/" ~ "app.d"]){ + if( existsFile(p ~ fil) ) { + app_main_file = (Path(sf) ~ fil).toNativeString(); + break; + } + } + } + + // generate default configurations if none are defined + if (m_info.configurations.length == 0) { + if (bs.targetType == TargetType.executable) { + BuildSettingsTemplate app_settings; + app_settings.targetType = TargetType.executable; + if (bs.mainSourceFile.empty) app_settings.mainSourceFile = app_main_file; + m_info.configurations ~= ConfigurationInfo("application", app_settings); + } else if (bs.targetType != TargetType.none) { + BuildSettingsTemplate lib_settings; + lib_settings.targetType = bs.targetType == TargetType.autodetect ? TargetType.library : bs.targetType; + + if (bs.targetType == TargetType.autodetect) { + if (app_main_file.length) { + lib_settings.excludedSourceFiles[""] ~= app_main_file; + + BuildSettingsTemplate app_settings; + app_settings.targetType = TargetType.executable; + app_settings.mainSourceFile = app_main_file; + m_info.configurations ~= ConfigurationInfo("application", app_settings); + } + } + + m_info.configurations ~= ConfigurationInfo("library", lib_settings); + } + } + } + private void simpleLint() const { if (m_parentPackage) { if (m_parentPackage.path != path) { @@ -467,7 +481,7 @@ logWarn("License in subpackage %s is different than it's parent package, this is discouraged.", name); } } - if (name.empty()) logWarn("The package in %s has no name.", path); + if (name.empty) logWarn("The package in %s has no name.", path); } private static RawPackage rawPackageFromFile(PathAndFormat file, bool silent_fail = false) { diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 41e0532..ff03744 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -287,8 +287,8 @@ /// destination and sets a version field in the package description. Package storeFetchedPackage(Path zip_file_path, Json package_info, Path destination) { - auto package_name = package_info.name.get!string(); - auto package_version = package_info["version"].get!string(); + auto package_name = package_info.name.get!string; + auto package_version = package_info["version"].get!string; auto clean_package_version = package_version[package_version.startsWith("~") ? 1 : 0 .. $]; logDiagnostic("Placing package '%s' version '%s' to location '%s' from file '%s'", @@ -330,7 +330,6 @@ // extract & place mkdirRecurse(destination.toNativeString()); - auto journal = new Journal; logDiagnostic("Copying all files..."); int countFiles = 0; foreach(ArchiveMember a; archive.directory) { @@ -342,14 +341,12 @@ if( dst_path.endsWithSlash ){ if( !existsDirectory(dst_path) ) mkdirRecurse(dst_path.toNativeString()); - journal.add(Journal.Entry(Journal.Type.Directory, cleanedPath)); } else { if( !existsDirectory(dst_path.parentPath) ) mkdirRecurse(dst_path.parentPath.toNativeString()); auto dstFile = openFile(dst_path, FileMode.CreateTrunc); scope(exit) dstFile.close(); dstFile.put(archive.expand(a)); - journal.add(Journal.Entry(Journal.Type.RegularFile, cleanedPath)); ++countFiles; } } @@ -358,21 +355,11 @@ // overwrite dub.json (this one includes a version field) auto pack = new Package(destination, PathAndFormat(), null, package_info["version"].get!string); - if (pack.packageInfoFilename.head != defaultPackageFilename()) { + if (pack.packageInfoFilename.head != defaultPackageFilename) // Storeinfo saved a default file, this could be different to the file from the zip. removeFile(pack.packageInfoFilename); - journal.remove(Journal.Entry(Journal.Type.RegularFile, Path(pack.packageInfoFilename.head))); - journal.add(Journal.Entry(Journal.Type.RegularFile, Path(defaultPackageFilename()))); - } pack.storeInfo(); - - // Write journal - logDebug("Saving retrieval action journal..."); - journal.add(Journal.Entry(Journal.Type.RegularFile, Path(JournalJsonFilename))); - journal.save(destination ~ JournalJsonFilename); - addPackages(m_packages, pack); - return pack; } @@ -382,55 +369,6 @@ logDebug("Remove %s, version %s, path '%s'", pack.name, pack.vers, pack.path); enforce(!pack.path.empty, "Cannot remove package "~pack.name~" without a path."); - // delete package files physically - logDebug("Looking up journal"); - auto journalFile = pack.path~JournalJsonFilename; - if (!existsFile(journalFile)) - throw new Exception(format("Removal failed, no retrieval journal found for '%s'. Please remove the folder '%s' manually.", pack.name, pack.path.toNativeString())); - - auto packagePath = pack.path; - auto journal = new Journal(journalFile); - - - // Determine all target paths/files - /*auto basebs = pack.getBuildSettings(); - foreach (conf; pack.configurations) { - auto bs = pack.getBuildSettings(conf); - auto tpath = conf.targetPath.length ? conf.targetPath : basebs.targetPath; - auto tname = conf.targetName.length ? conf.targetName : basebs.targetName; - auto ttype = conf.targetType != TargetType.auto_ ? conf.targetType : basebs.targetType; - if (ttype == TargetType.none || ttype == TargetType.auto_) continue; - foreach (n; generatePlatformNames(tname, ttype)) - // ... - }*/ - - // test if there are any untracked files - if (!force_remove) { - void checkFilesRec(Path p) - { - // TODO: ignore target paths/files - - foreach (fi; iterateDirectory(p)) { - auto fpath = p ~ fi.name; - if (fi.isDirectory) { - // Indicate a directory. - fpath.endsWithSlash(true); - // Ignore /.dub folder: This folder and its content - // are not tracked by the Journal. - if (fpath.relativeTo(pack.path) == Path(".dub/")) - continue; - checkFilesRec(fpath); - } - - auto type = fi.isDirectory ? Journal.Type.Directory : Journal.Type.RegularFile; - if (!journal.containsEntry(type, fpath.relativeTo(pack.path))) - throw new Exception("Untracked file found, aborting package removal, file: " - ~ fpath.toNativeString() ~ "\nPlease remove the package folder manually or use --force-remove."); - } - } - checkFilesRec(pack.path); - } - // remove package from repositories' list bool found = false; bool removeFrom(Package[] packs, in Package pack) { @@ -538,12 +476,12 @@ enforce(packlist.type == Json.Type.array, LocalPackagesFilename~" must contain an array."); foreach( pentry; packlist ){ try { - auto name = pentry.name.get!string(); - auto path = Path(pentry.path.get!string()); + auto name = pentry.name.get!string; + auto path = Path(pentry.path.get!string); if (name == "*") { paths ~= path; } else { - auto ver = Version(pentry["version"].get!string()); + auto ver = Version(pentry["version"].get!string); Package pp; if (!refresh_existing_packages) { @@ -667,7 +605,7 @@ } auto hash = sha1.finish(); logDebug("Project hash: %s", hash); - return hash[0..$]; + return hash[].dup; } private void writeLocalPackageList(LocalPackageType type) @@ -772,7 +710,6 @@ system } -enum JournalJsonFilename = "journal.json"; enum LocalPackagesFilename = "local-packages.json"; enum LocalOverridesFilename = "local-overrides.json"; @@ -790,97 +727,3 @@ this.packagePath = path ~"packages/"; } } - - -/* - Retrieval journal for later removal, keeping track of placed files - files. - - Example Json: - --- - { - "version": 1, - "files": { - "file1": "typeoffile1", - ... - } - } - --- -*/ -private class Journal { - private enum Version = 1; - - enum Type { - RegularFile, - Directory, - Alien - } - - struct Entry { - this( Type t, Path f ) { type = t; relFilename = f; } - Type type; - Path relFilename; - } - - @property const(Entry[]) entries() const { return m_entries; } - - this() {} - - /// Initializes a Journal from a json file. - this(Path journalFile) { - auto jsonJournal = jsonFromFile(journalFile); - enforce(cast(int)jsonJournal["Version"] == Version, "Mismatched version: "~to!string(cast(int)jsonJournal["Version"]) ~ "vs. " ~to!string(Version)); - foreach(string file, type; jsonJournal["Files"]) - m_entries ~= Entry(to!Type(cast(string)type), Path(file)); - } - - void add(Entry e) { - foreach(Entry ent; entries) { - if( e.relFilename == ent.relFilename ) { - enforce(e.type == ent.type, "Duplicate('"~to!string(e.relFilename)~"'), different types: "~to!string(e.type)~" vs. "~to!string(ent.type)); - return; - } - } - m_entries ~= e; - } - - void remove(Entry e) { - foreach(i, Entry ent; entries) { - if( e.relFilename == ent.relFilename ) { - m_entries = std.algorithm.remove(m_entries, i); - return; - } - } - enforce(false, "Cannot remove entry, not available: " ~ e.relFilename.toNativeString()); - } - - /// Save the current state to the path. - void save(Path path) { - Json jsonJournal = serialize(); - auto fileJournal = openFile(path, FileMode.CreateTrunc); - scope(exit) fileJournal.close(); - fileJournal.writePrettyJsonString(jsonJournal); - } - - bool containsEntry(Type type, Path path) - const { - foreach (e; entries) - if (e.type == type && e.relFilename == path) - return true; - return false; - } - - private Json serialize() const { - Json[string] files; - foreach(Entry e; m_entries) - files[to!string(e.relFilename)] = to!string(e.type); - Json[string] json; - json["Version"] = Version; - json["Files"] = files; - return Json(json); - } - - private { - Entry[] m_entries; - } -} diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index c087713..12e9b04 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -41,9 +41,6 @@ /// perform cache operation void cacheOp(Path cacheDir, CacheOp op); - - /// get all package names for this supplier - string[] getPackageNames(); } /// operations on package supplier cache @@ -95,10 +92,6 @@ void cacheOp(Path cacheDir, CacheOp op) { } - string[] getPackageNames(){ - assert(false, "FileSystemPackageSupplier.getPackageNames, not implemented yet"); - } - private Path bestPackageFile(string packageId, Dependency dep, bool pre_release) { Path toPath(Version ver) { @@ -155,23 +148,11 @@ download(url, path); } - Json getPackageDescription(string packageId, Dependency dep, bool pre_release) { return getBestPackage(packageId, dep, pre_release); } - string[] getPackageNames(){ - import std.array : replace; - auto url = m_registryUrl ~ Path(PackagesPath~"/index.json"); - Json data = (cast(string)download(url)).parseJsonString(); - string[] packages; - foreach(ele; data){ - packages ~= ele.toString().replace("\"", ""); - } - return packages; - } - void cacheOp(Path cacheDir, CacheOp op) { auto path = cacheDir ~ cacheFileName; diff --git a/source/dub/project.d b/source/dub/project.d index ee2cc78..f71da6e 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -245,16 +245,18 @@ logDiagnostic("Error getting sub package %s: %s", name, e.msg); continue; } - } else { - if (!m_selections.hasSelectedVersion(basename)) { - logDiagnostic("Version selection for dependency %s (%s) of %s is missing.", - basename, name, pack.name); - continue; - } - + } else if (m_selections.hasSelectedVersion(basename)) { vspec = m_selections.getSelectedVersion(basename); - p = m_packageManager.getBestPackage(name, vspec); + } else if (m_dependencies.canFind!(d => getBasePackageName(d.name) == basename)) { + auto idx = m_dependencies.countUntil!(d => getBasePackageName(d.name) == basename); + auto bp = m_dependencies[idx].basePackage; + vspec = Dependency(bp.path); + p = m_packageManager.getSubPackage(bp, getSubPackageName(name), false); + } else { + logDiagnostic("Version selection for dependency %s (%s) of %s is missing.", + basename, name, pack.name); + continue; } } @@ -936,7 +938,7 @@ if (j.type == Json.Type.string) return Dependency(Version(j.get!string)); else if (j.type == Json.Type.object) - return Dependency(Path(j.path.get!string())); + return Dependency(Path(j.path.get!string)); else throw new Exception(format("Unexpected type for dependency: %s", j.type)); } diff --git a/source/dub/recipe/json.d b/source/dub/recipe/json.d index 1e5e013..ccf2a59 100644 --- a/source/dub/recipe/json.d +++ b/source/dub/recipe/json.d @@ -133,7 +133,7 @@ switch (name) { default: break; case "name": - config.name = value.get!string(); + config.name = value.get!string; enforce(!config.name.empty, "Configurations must have a non-empty name."); break; case "platforms": config.platforms = deserializeJson!(string[])(value); break; @@ -160,7 +160,7 @@ { auto idx = std.string.indexOf(name, "-"); string basename, suffix; - if( idx >= 0 ) basename = name[0 .. idx], suffix = name[idx .. $]; + if( idx >= 0 ) { basename = name[0 .. idx]; suffix = name[idx .. $]; } else basename = name; switch(basename){ default: break; @@ -179,7 +179,7 @@ break; case "targetType": enforce(suffix.empty, "targetType does not support platform customization."); - bs.targetType = value.get!string().to!TargetType(); + bs.targetType = value.get!string.to!TargetType; break; case "targetPath": enforce(suffix.empty, "targetPath does not support platform customization."); @@ -207,7 +207,7 @@ case "files": case "sourceFiles": bs.sourceFiles[suffix] = deserializeJson!(string[])(value); break; case "sourcePaths": bs.sourcePaths[suffix] = deserializeJson!(string[])(value); break; - case "sourcePath": bs.sourcePaths[suffix] ~= [value.get!string()]; break; // deprecated + case "sourcePath": bs.sourcePaths[suffix] ~= [value.get!string]; break; // deprecated case "excludedSourceFiles": bs.excludedSourceFiles[suffix] = deserializeJson!(string[])(value); break; case "copyFiles": bs.copyFiles[suffix] = deserializeJson!(string[])(value); break; case "versions": bs.versions[suffix] = deserializeJson!(string[])(value); break; diff --git a/test/0-init-fail/.gitignore b/test/0-init-fail/.gitignore new file mode 100644 index 0000000..433d266 --- /dev/null +++ b/test/0-init-fail/.gitignore @@ -0,0 +1,5 @@ +.dub +docs.json +__dummy.html +*.o +*.obj diff --git a/test/0-init-fail/0-init-fail.sh b/test/0-init-fail/0-init-fail.sh new file mode 100755 index 0000000..11fadf4 --- /dev/null +++ b/test/0-init-fail/0-init-fail.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +packname="0-init-fail-pack" +deps="logger PACKAGE_DONT_EXIST" # would be very unlucky if it does exist... + +$DUB init $packname $deps + +function cleanup { + rm -rf $packname +} + +if [ -e $packname/dub.json ]; then # package is there, it should have failed + cleanup + exit 1 +fi +exit 0 diff --git a/test/0-init-fail/dub.json b/test/0-init-fail/dub.json new file mode 100644 index 0000000..6e6605b --- /dev/null +++ b/test/0-init-fail/dub.json @@ -0,0 +1,8 @@ +{ + "name": "0-init-fail", + "description": "A minimal D application.", + "copyright": "Copyright © 2014, colin", + "authors": ["colin"], + "dependencies": { + } +} diff --git a/test/0-init-fail/source/app.d b/test/0-init-fail/source/app.d new file mode 100644 index 0000000..0569360 --- /dev/null +++ b/test/0-init-fail/source/app.d @@ -0,0 +1,10 @@ +import std.stdio; + +import std.process : execute; +int main(string[] args) +{ + writefln("Executing init test - fail"); + auto script = args[0] ~ ".sh"; + auto dubInit = execute(script); + return dubInit.status; +} diff --git a/test/3-copyFiles/data/file_to_copy.txt b/test/3-copyFiles/data/file_to_copy.txt new file mode 100644 index 0000000..ece63c0 --- /dev/null +++ b/test/3-copyFiles/data/file_to_copy.txt @@ -0,0 +1 @@ +file_to_copy.txt content diff --git a/test/3-copyFiles/data/file_to_copy_mask1.txt b/test/3-copyFiles/data/file_to_copy_mask1.txt new file mode 100644 index 0000000..1d3b950 --- /dev/null +++ b/test/3-copyFiles/data/file_to_copy_mask1.txt @@ -0,0 +1 @@ +file_to_copy_mask1.txt content \ No newline at end of file diff --git a/test/3-copyFiles/data/file_to_copy_mask2.txt b/test/3-copyFiles/data/file_to_copy_mask2.txt new file mode 100644 index 0000000..49421e2 --- /dev/null +++ b/test/3-copyFiles/data/file_to_copy_mask2.txt @@ -0,0 +1 @@ +file_to_copy_mask2.txt content diff --git a/test/3-copyFiles/data/res/.nocopy/file_inside_dot_prefixed_dir.txt b/test/3-copyFiles/data/res/.nocopy/file_inside_dot_prefixed_dir.txt new file mode 100644 index 0000000..2ef267e --- /dev/null +++ b/test/3-copyFiles/data/res/.nocopy/file_inside_dot_prefixed_dir.txt @@ -0,0 +1 @@ +some content diff --git a/test/3-copyFiles/data/res/hdpi/file1.txt b/test/3-copyFiles/data/res/hdpi/file1.txt new file mode 100644 index 0000000..20d74d4 --- /dev/null +++ b/test/3-copyFiles/data/res/hdpi/file1.txt @@ -0,0 +1 @@ +hdpi/file1 content diff --git a/test/3-copyFiles/data/res/hdpi/file2.txt b/test/3-copyFiles/data/res/hdpi/file2.txt new file mode 100644 index 0000000..e313178 --- /dev/null +++ b/test/3-copyFiles/data/res/hdpi/file2.txt @@ -0,0 +1 @@ +hdpi/file2 content diff --git a/test/3-copyFiles/data/res/hdpi/file3.txt b/test/3-copyFiles/data/res/hdpi/file3.txt new file mode 100644 index 0000000..8f9119d --- /dev/null +++ b/test/3-copyFiles/data/res/hdpi/file3.txt @@ -0,0 +1 @@ +hdpi/file3 content diff --git a/test/3-copyFiles/data/res/hdpi/nested_dir/nested_file.txt b/test/3-copyFiles/data/res/hdpi/nested_dir/nested_file.txt new file mode 100644 index 0000000..3bd1ae7 --- /dev/null +++ b/test/3-copyFiles/data/res/hdpi/nested_dir/nested_file.txt @@ -0,0 +1 @@ +hdpi/nested_dir/nested_file.txt content diff --git a/test/3-copyFiles/data/res/i18n/resource_en.txt b/test/3-copyFiles/data/res/i18n/resource_en.txt new file mode 100644 index 0000000..000868e --- /dev/null +++ b/test/3-copyFiles/data/res/i18n/resource_en.txt @@ -0,0 +1 @@ +i18n - english resources \ No newline at end of file diff --git a/test/3-copyFiles/data/res/i18n/resource_fr.txt b/test/3-copyFiles/data/res/i18n/resource_fr.txt new file mode 100644 index 0000000..26b25f2 --- /dev/null +++ b/test/3-copyFiles/data/res/i18n/resource_fr.txt @@ -0,0 +1 @@ +i18n - french resources diff --git a/test/3-copyFiles/data/res/ldpi/file1.txt b/test/3-copyFiles/data/res/ldpi/file1.txt new file mode 100644 index 0000000..6a90d50 --- /dev/null +++ b/test/3-copyFiles/data/res/ldpi/file1.txt @@ -0,0 +1 @@ +ldpi/file1 content diff --git a/test/3-copyFiles/data/res/ldpi/file2.txt b/test/3-copyFiles/data/res/ldpi/file2.txt new file mode 100644 index 0000000..5da1d05 --- /dev/null +++ b/test/3-copyFiles/data/res/ldpi/file2.txt @@ -0,0 +1 @@ +ldpi/file2 content diff --git a/test/3-copyFiles/data/res/ldpi/file3.txt b/test/3-copyFiles/data/res/ldpi/file3.txt new file mode 100644 index 0000000..acca5e8 --- /dev/null +++ b/test/3-copyFiles/data/res/ldpi/file3.txt @@ -0,0 +1 @@ +ldpi/file3 content diff --git a/test/3-copyFiles/data/res/mdpi/file1.txt b/test/3-copyFiles/data/res/mdpi/file1.txt new file mode 100644 index 0000000..57586c8 --- /dev/null +++ b/test/3-copyFiles/data/res/mdpi/file1.txt @@ -0,0 +1 @@ +mdpi/file1 content diff --git a/test/3-copyFiles/data/res/mdpi/file2.txt b/test/3-copyFiles/data/res/mdpi/file2.txt new file mode 100644 index 0000000..6435c1e --- /dev/null +++ b/test/3-copyFiles/data/res/mdpi/file2.txt @@ -0,0 +1 @@ +mdpi/file2 content diff --git a/test/3-copyFiles/data/res/mdpi/file3.txt b/test/3-copyFiles/data/res/mdpi/file3.txt new file mode 100644 index 0000000..fb88bd0 --- /dev/null +++ b/test/3-copyFiles/data/res/mdpi/file3.txt @@ -0,0 +1 @@ +mdpi/file3 content diff --git a/test/3-copyFiles/dub.json b/test/3-copyFiles/dub.json new file mode 100644 index 0000000..e9216ff --- /dev/null +++ b/test/3-copyFiles/dub.json @@ -0,0 +1,10 @@ +{ + "name": "copyfiles-test", + "targetType": "executable", + "targetPath": "bin", + "copyFiles": [ + "data/res", + "data/res/*dpi", + "data/file_to_copy.txt", + "data/file_to_copy_mask*.txt"] +} diff --git a/test/3-copyFiles/source/app.d b/test/3-copyFiles/source/app.d new file mode 100644 index 0000000..a39cc7b --- /dev/null +++ b/test/3-copyFiles/source/app.d @@ -0,0 +1,24 @@ +import std.algorithm, std.array, std.file, std.path; + +void main(string[] args) +{ + immutable root = args[0].dirName; // get the bin dir + immutable pfx = root.length + "/".length; + auto files = dirEntries(root, SpanMode.breadth).map!(n => n[pfx .. $]).array.sort().release; + + assert(files == + [ + "copyfiles-test", "file_to_copy.txt", "file_to_copy_mask1.txt", + "file_to_copy_mask2.txt", "hdpi", "hdpi/file1.txt", "hdpi/file2.txt", + "hdpi/file3.txt", "hdpi/nested_dir", "hdpi/nested_dir/nested_file.txt", + "ldpi", "ldpi/file1.txt", "ldpi/file2.txt", "ldpi/file3.txt", "mdpi", + "mdpi/file1.txt", "mdpi/file2.txt", "mdpi/file3.txt", "res", + "res/.nocopy", "res/.nocopy/file_inside_dot_prefixed_dir.txt", + "res/hdpi", "res/hdpi/file1.txt", "res/hdpi/file2.txt", "res/hdpi/file3.txt", + "res/hdpi/nested_dir", "res/hdpi/nested_dir/nested_file.txt", "res/i18n", + "res/i18n/resource_en.txt", "res/i18n/resource_fr.txt", "res/ldpi", + "res/ldpi/file1.txt", "res/ldpi/file2.txt", "res/ldpi/file3.txt", "res/mdpi", + "res/mdpi/file1.txt", "res/mdpi/file2.txt", "res/mdpi/file3.txt" + ], files.join(", ") + ); +} diff --git a/test/custom-source-main-bug487/.gitignore b/test/custom-source-main-bug487/.gitignore new file mode 100644 index 0000000..433d266 --- /dev/null +++ b/test/custom-source-main-bug487/.gitignore @@ -0,0 +1,5 @@ +.dub +docs.json +__dummy.html +*.o +*.obj diff --git a/test/custom-source-main-bug487/.no_run b/test/custom-source-main-bug487/.no_run new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/custom-source-main-bug487/.no_run diff --git a/test/custom-source-main-bug487/.no_test b/test/custom-source-main-bug487/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/custom-source-main-bug487/.no_test diff --git a/test/custom-source-main-bug487/dub.json b/test/custom-source-main-bug487/dub.json new file mode 100644 index 0000000..572c35c --- /dev/null +++ b/test/custom-source-main-bug487/dub.json @@ -0,0 +1,6 @@ +{ + "name": "custom-source-main-bug487", + "sourcePaths": ["mysrc"], + "dependencies": { + } +} diff --git a/test/custom-source-main-bug487/mysrc/app.d b/test/custom-source-main-bug487/mysrc/app.d new file mode 100644 index 0000000..9198103 --- /dev/null +++ b/test/custom-source-main-bug487/mysrc/app.d @@ -0,0 +1,3 @@ +void main() +{ +} diff --git a/test/run-unittest.sh b/test/run-unittest.sh index 0581901..a308ef4 100755 --- a/test/run-unittest.sh +++ b/test/run-unittest.sh @@ -20,6 +20,12 @@ CURR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +for script in $(ls $CURR_DIR/*.sh); do + if [ "$script" = "$(readlink -f ${BASH_SOURCE[0]})" ]; then continue; fi + log "Running $script..." + $script || die "Script failure." +done + for pack in $(ls -d $CURR_DIR/*/); do # First we build the packages if [ ! -e $pack/.no_build ]; then # For sourceLibrary diff --git a/test/test-version-opt.sh b/test/test-version-opt.sh new file mode 100755 index 0000000..2931868 --- /dev/null +++ b/test/test-version-opt.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +$DUB --version | grep -qF 'DUB version'