diff --git a/.github/issue_template.md b/.github/issue_template.md index b63759d..b3f6c17 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,11 +1,15 @@ -Please search for existing solutions to your problem. + ### System information -- **dub version** (e.g. dub 1.3.0) -- **OS Platform and distribution** (e.g. Windows 10, Linux Ubuntu 16.04) + +- **dub version**: (e.g. dub 1.3.0) +- **OS Platform and distribution**: (e.g. Windows 10, Linux Ubuntu 16.04) - **compiler version** (e.g. dmd-2.074.1) ### Bug Description diff --git a/.travis.yml b/.travis.yml index d6b50cc..86f505a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,8 +30,6 @@ env: [FRONTEND=2.068] - d: dmd-2.067.1 env: [FRONTEND=2.067] - - d: dmd-2.066.1 - env: [FRONTEND=2.066] - d: ldc-beta env: [FRONTEND=2.073] - d: ldc @@ -48,10 +46,8 @@ env: [FRONTEND=2.068] - d: gdc env: [FRONTEND=2.068] - - d: gdc-5.2.0 - env: [FRONTEND=2.066] - - d: gdc-4.9.2 - env: [FRONTEND=2.066] + - d: gdc-4.8.5 + env: [FRONTEND=2.068] allow_failures: - d: gdc diff --git a/dub.sdl b/dub.sdl index 3155a00..85fcaba 100644 --- a/dub.sdl +++ b/dub.sdl @@ -15,7 +15,6 @@ configuration "library" { targetType "library" - libs "curl" excludedSourceFiles "source/app.d" copyFiles "bin/libcurl.dll" "bin/libeay32.dll" "bin/ssleay32.dll" platform="windows" versions "DubUseCurl" diff --git a/dub.selections.json b/dub.selections.json index cbde831..dd9a304 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -3,15 +3,15 @@ "versions": { "botan": "1.12.9", "botan-math": "1.0.3", - "diet-ng": "1.4.1", - "eventcore": "0.8.14", + "diet-ng": "1.4.3", + "eventcore": "0.8.26", "libasync": "0.8.3", "libev": "5.0.0+4.04", "libevent": "2.0.2+2.0.16", "memutils": "0.4.9", - "openssl": "1.1.5+1.0.1g", - "taggedalgebraic": "0.10.7", - "vibe-core": "1.0.0", - "vibe-d": "0.8.0" + "openssl": "1.1.6+1.0.1g", + "taggedalgebraic": "0.10.8", + "vibe-core": "1.3.0", + "vibe-d": "0.8.3-alpha.1" } } diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 7da2883..e96db83 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -218,8 +218,8 @@ // initialize the root package if (!cmd.skipDubInitialization) { if (options.bare) { - dub = new Dub(Path(getcwd())); - dub.rootPath = Path(options.root_path); + dub = new Dub(NativePath(getcwd())); + dub.rootPath = NativePath(options.root_path); dub.defaultPlacementLocation = options.placementLocation; } else { // initialize DUB @@ -230,7 +230,7 @@ // make the CWD package available so that for example sub packages can reference their // parent package. - try dub.packageManager.getOrLoadPackage(Path(options.root_path)); + try dub.packageManager.getOrLoadPackage(NativePath(options.root_path)); catch (Exception e) { logDiagnostic("No package found in current working directory."); } } } @@ -268,10 +268,11 @@ args.getopt("root", &root_path, ["Path to operate in instead of the current working dir"]); args.getopt("registry", ®istry_urls, ["Search the given DUB registry URL first when resolving dependencies. Can be specified multiple times."]); args.getopt("skip-registry", &skipRegistry, [ - "Skips searching certain package registries for dependencies:", - " none: Search all configured registries (default)", - " standard: Don't search on "~defaultRegistryURL, - " all: Search none of the configured registries", + "Sets a mode for skipping the search on certain package registry types:", + " none: Search all configured or default registries (default)", + " standard: Don't search the main registry (e.g. "~defaultRegistryURL~")", + " configured: Skip all default and user configured registries", + " all: Only search registries specified with --registry", ]); args.getopt("annotate", &annotate, ["Do not perform any action, just print what would be done"]); args.getopt("bare", &bare, ["Read only packages contained in the current directory"]); @@ -544,7 +545,7 @@ logInfo("Deprecated use of init type. Use --type=[vibe.d | deimos | minimal] in future."); } } - dub.createEmptyPackage(Path(dir), free_args, m_templateType, m_format, &depCallback); + dub.createEmptyPackage(NativePath(dir), free_args, m_templateType, m_format, &depCallback); logInfo("Package successfully created in %s", dir.length ? dir : "."); return 0; @@ -929,7 +930,7 @@ settings.run = true; settings.runArgs = app_args; - dub.testProject(settings, m_buildConfig, Path(m_mainFile)); + dub.testProject(settings, m_buildConfig, NativePath(m_mainFile)); return 0; } } @@ -1022,7 +1023,7 @@ // disable all log output to stdout and use "writeln" to output the JSON description auto ll = getLogLevel(); - setLogLevel(LogLevel.warn); + setLogLevel(max(ll, LogLevel.warn)); scope (exit) setLogLevel(ll); string package_name; @@ -1528,9 +1529,9 @@ auto scope_ = m_system ? LocalPackageType.system : LocalPackageType.user; auto pack = free_args[0]; auto ver = Dependency(free_args[1]); - if (existsFile(Path(free_args[2]))) { - auto target = Path(free_args[2]); - if (!target.absolute) target = Path(getcwd()) ~ target; + if (existsFile(NativePath(free_args[2]))) { + auto target = NativePath(free_args[2]); + if (!target.absolute) target = NativePath(getcwd()) ~ target; dub.packageManager.addOverride(scope_, pack, ver, target); logInfo("Added override %s %s => %s", pack, ver, target); } else { @@ -1679,7 +1680,7 @@ import std.format : formattedWrite; if (m_testPackage.length) { - dub = new Dub(Path(getcwd())); + dub = new Dub(NativePath(getcwd())); setupPackage(dub, m_testPackage); m_defaultConfig = dub.project.getDefaultConfiguration(m_buildPlatform); @@ -1708,10 +1709,10 @@ } } else { enforceUsage(free_args.length == 1, "Expected destination path."); - auto path = Path(free_args[0]); + auto path = NativePath(free_args[0]); path.normalize(); - enforceUsage(path.length > 0, "Destination path must not be empty."); - if (!path.absolute) path = Path(getcwd()) ~ path; + enforceUsage(!path.empty, "Destination path must not be empty."); + if (!path.absolute) path = NativePath(getcwd()) ~ path; enforceUsage(!path.startsWith(dub.rootPath), "Destination path must not be a sub directory of the tested package!"); setupPackage(dub, null); @@ -1719,7 +1720,7 @@ if (m_buildConfig.empty) m_buildConfig = prj.getDefaultConfiguration(m_buildPlatform); - void copyFolderRec(Path folder, Path dstfolder) + void copyFolderRec(NativePath folder, NativePath dstfolder) { mkdirRecurse(dstfolder.toNativeString()); foreach (de; iterateDirectory(folder.toNativeString())) { @@ -1738,13 +1739,13 @@ } static void fixPathDependency(string pack, ref Dependency dep) { - if (dep.path.length > 0) { + if (!dep.path.empty) { auto mainpack = getBasePackageName(pack); - dep.path = Path("../") ~ mainpack; + dep.path = NativePath("../") ~ mainpack; } } - void fixPathDependencies(ref PackageRecipe recipe, Path base_path) + void fixPathDependencies(ref PackageRecipe recipe, NativePath base_path) { foreach (name, ref dep; recipe.buildSettings.dependencies) fixPathDependency(name, dep); @@ -1755,7 +1756,7 @@ foreach (ref subp; recipe.subPackages) if (subp.path.length) { - auto sub_path = base_path ~ Path(subp.path); + auto sub_path = base_path ~ NativePath(subp.path); auto pack = prj.packageManager.getOrLoadPackage(sub_path); fixPathDependencies(pack.recipe, sub_path); pack.storeInfo(sub_path); diff --git a/source/dub/compilers/buildsettings.d b/source/dub/compilers/buildsettings.d index 3da87b1..9f54140 100644 --- a/source/dub/compilers/buildsettings.d +++ b/source/dub/compilers/buildsettings.d @@ -12,8 +12,7 @@ import std.array : array; import std.algorithm : filter; import std.path : globMatch; -static if (__VERSION__ >= 2067) - import std.typecons : BitFlags; +import std.typecons : BitFlags; /// BuildPlatform specific settings, like needed libraries or additional @@ -168,9 +167,9 @@ static void addSI(ref string[] arr, in string[] vals) { bool[string] existing; - foreach (v; arr) existing[Path(v).head.toString()] = true; + foreach (v; arr) existing[NativePath(v).head.toString()] = true; foreach (v; vals) { - auto s = Path(v).head.toString(); + auto s = NativePath(v).head.toString(); if (s !in existing) { existing[s] = true; arr ~= v; @@ -192,7 +191,7 @@ bool matches(string s) { foreach (p; vals) - if (Path(s) == Path(p) || globMatch(s, p)) + if (NativePath(s) == NativePath(p) || globMatch(s, p)) return true; return false; } @@ -284,26 +283,9 @@ struct BuildRequirements { import dub.internal.vibecompat.data.serialization : ignore; - static if (__VERSION__ >= 2067) { - @ignore BitFlags!BuildRequirement values; - this(BuildRequirement req) { values = req; } - } else { - @ignore BuildRequirement values; - this(BuildRequirement req) { values = req; } - BuildRequirement[] toRepresentation() - const { - BuildRequirement[] ret; - for (int f = 1; f <= BuildRequirement.max; f *= 2) - if (values & f) ret ~= cast(BuildRequirement)f; - return ret; - } - static BuildRequirements fromRepresentation(BuildRequirement[] v) - { - BuildRequirements ret; - foreach (f; v) ret.values |= f; - return ret; - } - } + @ignore BitFlags!BuildRequirement values; + this(BuildRequirement req) { values = req; } + alias values this; } @@ -340,27 +322,9 @@ struct BuildOptions { import dub.internal.vibecompat.data.serialization : ignore; - static if (__VERSION__ >= 2067) { - @ignore BitFlags!BuildOption values; - this(BuildOption opt) { values = opt; } - this(BitFlags!BuildOption v) { values = v; } - } else { - @ignore BuildOption values; - this(BuildOption opt) { values = opt; } - BuildOption[] toRepresentation() - const { - BuildOption[] ret; - for (int f = 1; f <= BuildOption.max; f *= 2) - if (values & f) ret ~= cast(BuildOption)f; - return ret; - } - static BuildOptions fromRepresentation(BuildOption[] v) - { - BuildOptions ret; - foreach (f; v) ret.values |= f; - return ret; - } - } + @ignore BitFlags!BuildOption values; + this(BuildOption opt) { values = opt; } + this(BitFlags!BuildOption v) { values = v; } alias values this; } diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index 28367d0..f70556d 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -192,7 +192,7 @@ } if (tpath is null) - tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); + tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); settings.addDFlags("-of"~tpath); } @@ -210,7 +210,7 @@ void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) { import std.string; - auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); + auto tpath = NativePath(settings.targetPath) ~ getTargetFileName(settings, platform); auto args = ["-of"~tpath.toNativeString()]; args ~= objects; args ~= settings.sourceFiles; diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index 1718cad..063c60e 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -182,7 +182,7 @@ } if (tpath is null) - tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); + tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); settings.addDFlags("-o", tpath); } diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index 7d25884..dbc77fd 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -195,7 +195,7 @@ } if (tpath is null) - tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); + tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); settings.addDFlags("-of"~tpath); } diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index f28f878..81983ee 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -242,7 +242,7 @@ See_Also: `readPlatformProbe` */ -Path generatePlatformProbeFile() +NativePath generatePlatformProbeFile() { import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.data.json; diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 40b4863..cd6c01d 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -44,7 +44,7 @@ package name is notably not part of the dependency specification. */ struct Dependency { -@trusted: // Too many issues on DMD 2.065.0 to annotate with @safe +@safe: private { // Shortcut to create >=0.0.0 @@ -53,7 +53,7 @@ Version m_versA; bool m_inclusiveB = true; // B comparison < (true) or <= (false) Version m_versB; - Path m_path; + NativePath m_path; bool m_optional = false; bool m_default = false; } @@ -87,16 +87,16 @@ /** Constructs a new dependency specification that matches a specific path. */ - this(Path path) + this(NativePath path) { this(ANY_IDENT); m_path = path; } /// If set, overrides any version based dependency selection. - @property void path(Path value) { m_path = value; } + @property void path(NativePath value) { m_path = value; } /// ditto - @property Path path() const { return m_path; } + @property NativePath path() const { return m_path; } /// Determines if the dependency is required or optional. @property bool optional() const { return m_optional; } @@ -239,8 +239,8 @@ based. Otherwise, the given `path` will be prefixed to the existing path. */ - Dependency mapToPath(Path path) - const { + Dependency mapToPath(NativePath path) + const @trusted { // NOTE Path is @system in vibe.d 0.7.x and in the compatibility layer if (m_path.empty || m_path.absolute) return this; else { Dependency ret = this; @@ -259,7 +259,12 @@ if (default_) ret ~= " (optional, default)"; else ret ~= " (optional)"; } - if (!path.empty) ret ~= " @"~path.toNativeString(); + + // NOTE Path is @system in vibe.d 0.7.x and in the compatibility layer + () @trusted { + if (!path.empty) ret ~= " @"~path.toNativeString(); + } (); + return ret; } @@ -270,7 +275,8 @@ represented as a JSON object with optional "version", "path", "optional" and "default" fields. */ - Json toJson() const { + Json toJson() + const @trusted { // NOTE Path and Json is @system in vibe.d 0.7.x and in the compatibility layer Json json; if( path.empty && !optional ){ json = Json(this.versionSpec); @@ -284,7 +290,7 @@ return json; } - unittest { + @trusted unittest { Dependency d = Dependency("==1.0.0"); assert(d.toJson() == Json("1.0.0"), "Failed: " ~ d.toJson().toPrettyString()); d = fromJson((fromJson(d.toJson())).toJson()); @@ -296,7 +302,8 @@ See `toJson` for a description of the JSON format. */ - static Dependency fromJson(Json verspec) { + static Dependency fromJson(Json verspec) + @trusted { // NOTE Path and Json is @system in vibe.d 0.7.x and in the compatibility layer Dependency dep; if( verspec.type == Json.Type.object ){ if( auto pp = "path" in verspec ) { @@ -304,7 +311,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 = NativePath(verspec["path"].get!string); } else { enforce("version" in verspec, "No version field specified!"); auto ver = verspec["version"].get!string; @@ -321,7 +328,7 @@ return dep; } - unittest { + @trusted unittest { assert(fromJson(parseJsonString("\">=1.0.0 <2.0.0\"")) == Dependency(">=1.0.0 <2.0.0")); Dependency parsed = fromJson(parseJsonString(` { @@ -334,7 +341,7 @@ Dependency d = Dependency.any; // supposed to ignore the version spec d.optional = true; d.default_ = true; - d.path = Path("path/to/package"); + d.path = NativePath("path/to/package"); assert(d == parsed); // optional and path not checked by opEquals. assert(d.optional == parsed.optional); @@ -367,11 +374,17 @@ } /// ditto - hash_t toHash() const nothrow @trusted { + hash_t toHash() + const nothrow @trusted { try { - auto strhash = &typeid(string).getHash; - auto str = this.toString(); - return strhash(&str); + size_t hash = 0; + hash = m_inclusiveA.hashOf(hash); + hash = m_versA.toString().hashOf(hash); + hash = m_inclusiveB.hashOf(hash); + hash = m_versB.toString().hashOf(hash); + hash = m_optional.hashOf(hash); + hash = m_default.hashOf(hash); + return hash; } catch (Exception) assert(false); } @@ -387,11 +400,18 @@ This is true in particular for the `any` constant. */ - bool matchesAny() const { - auto cmp = Dependency("*"); - cmp.optional = m_optional; - cmp.default_ = m_default; - return cmp == this; + bool matchesAny() + const { + return m_inclusiveA && m_inclusiveB + && m_versA.toString() == "0.0.0" + && m_versB == Version.maxRelease; + } + + unittest { + assert(Dependency("*").matchesAny); + assert(!Dependency(">0.0.0").matchesAny); + assert(!Dependency(">=1.0.0").matchesAny); + assert(!Dependency("<1.0.0").matchesAny); } /** Tests if the specification matches a specific version. @@ -427,20 +447,20 @@ const { if (this.matchesAny) return o; if (o.matchesAny) return this; - if (!this.valid || !o.valid) return invalid; if (m_versA.isBranch != o.m_versA.isBranch) return invalid; if (m_versB.isBranch != o.m_versB.isBranch) return invalid; if (m_versA.isBranch) return m_versA == o.m_versA ? this : invalid; - if (this.path != o.path) return invalid; + // NOTE Path is @system in vibe.d 0.7.x and in the compatibility layer + if (() @trusted { return this.path != o.path; } ()) return invalid; - Version a = m_versA > o.m_versA ? m_versA : o.m_versA; - Version b = m_versB < o.m_versB ? m_versB : o.m_versB; + int acmp = m_versA.opCmp(o.m_versA); + int bcmp = m_versB.opCmp(o.m_versB); Dependency d = this; - d.m_inclusiveA = !m_inclusiveA && m_versA >= o.m_versA ? false : o.m_inclusiveA; - d.m_versA = a; - d.m_inclusiveB = !m_inclusiveB && m_versB <= o.m_versB ? false : o.m_inclusiveB; - d.m_versB = b; + d.m_inclusiveA = !m_inclusiveA && acmp >= 0 ? false : o.m_inclusiveA; + d.m_versA = acmp > 0 ? m_versA : o.m_versA; + d.m_inclusiveB = !m_inclusiveB && bcmp <= 0 ? false : o.m_inclusiveB; + d.m_versB = bcmp < 0 ? m_versB : o.m_versB; d.m_optional = m_optional && o.m_optional; if (!d.valid) return invalid; @@ -640,24 +660,24 @@ struct Version { @safe: private { - enum MAX_VERS = "99999.0.0"; - enum UNKNOWN_VERS = "unknown"; + static immutable MAX_VERS = "99999.0.0"; + static immutable UNKNOWN_VERS = "unknown"; + static immutable masterString = "~master"; enum branchPrefix = '~'; - enum masterString = "~master"; string m_version; } - static @property Version minRelease() { return Version("0.0.0"); } - static @property Version maxRelease() { return Version(MAX_VERS); } - static @property Version masterBranch() { return Version(masterString); } - static @property Version unknown() { return Version(UNKNOWN_VERS); } + static immutable Version minRelease = Version("0.0.0"); + static immutable Version maxRelease = Version(MAX_VERS); + static immutable Version masterBranch = Version(masterString); + static immutable Version unknown = Version(UNKNOWN_VERS); /** Constructs a new `Version` from its string representation. */ this(string vers) { enforce(vers.length > 1, "Version strings must not be empty."); - if (vers[0] != branchPrefix && vers != UNKNOWN_VERS) + if (vers[0] != branchPrefix && vers.ptr !is UNKNOWN_VERS.ptr) enforce(vers.isValidVersion(), "Invalid SemVer format: " ~ vers); m_version = vers; } @@ -669,15 +689,10 @@ */ static Version fromString(string vers) { return Version(vers); } - bool opEquals(const Version oth) const { - if (isUnknown || oth.isUnknown) { - throw new Exception("Can't compare unknown versions! (this: %s, other: %s)".format(this, oth)); - } - return opCmp(oth) == 0; - } + bool opEquals(const Version oth) const { return opCmp(oth) == 0; } /// Tests if this represents a branch instead of a version. - @property bool isBranch() const { return !m_version.empty && m_version[0] == branchPrefix; } + @property bool isBranch() const { return m_version.length > 0 && m_version[0] == branchPrefix; } /// Tests if this represents the master branch "~master". @property bool isMaster() const { return m_version == masterString; } diff --git a/source/dub/dependencyresolver.d b/source/dub/dependencyresolver.d index a2102bd..f5e9dcb 100644 --- a/source/dub/dependencyresolver.d +++ b/source/dub/dependencyresolver.d @@ -14,6 +14,7 @@ import std.array : appender, array; import std.conv : to; import std.exception : enforce; +import std.typecons : Nullable; import std.string : format, indexOf, lastIndexOf; @@ -41,7 +42,7 @@ CONFIG config; hash_t toHash() const nothrow @trusted { - size_t ret = typeid(string).getHash(&pack); + size_t ret = pack.hashOf(); ret ^= typeid(CONFIG).getHash(&config); return ret; } @@ -123,7 +124,7 @@ config_indices[] = 0; visited = null; - sizediff_t validateConfigs(TreeNode parent, ref string error) + sizediff_t validateConfigs(TreeNode parent, ref ConflictError error) { import std.algorithm : max; @@ -141,7 +142,7 @@ // get the current config/version of the current dependency sizediff_t childidx = package_indices[basepack]; - if (all_configs[childidx] == [CONFIG.invalid]) { + if (all_configs[childidx].length == 1 && all_configs[childidx][0] == CONFIG.invalid) { // ignore invalid optional dependencies if (ch.depType != DependencyType.required) continue; @@ -160,7 +161,7 @@ } // choose another parent config to avoid the invalid child if (parentidx > maxcpi) { - error = format("Package %s contains invalid dependency %s (no version candidates)", parent.pack, ch.pack); + error = ConflictError(ConflictError.Kind.invalidDependency, parent, ch, CONFIG.invalid); logDiagnostic("%s (ci=%s)", error, parentidx); maxcpi = parentidx; } @@ -175,13 +176,13 @@ // if we are at the root level, we can safely skip the maxcpi computation and instead choose another childidx config if (parentbase == root_base_pack) { - error = format("No match for dependency %s %s of %s", ch.pack, ch.configs, parent.pack); + error = ConflictError(ConflictError.Kind.noRootMatch, parent, ch, config); return childidx; } if (childidx > maxcpi) { maxcpi = max(childidx, parentidx); - error = format("Dependency %s -> %s %s mismatches with selected version %s", parent.pack, ch.pack, ch.configs, config); + error = ConflictError(ConflictError.Kind.childMismatch, parent, ch, config); logDebug("%s (ci=%s)", error, maxcpi); } @@ -196,14 +197,26 @@ return maxcpi; } - string first_error; + Nullable!ConflictError first_error; + size_t loop_counter = 0; + + // Leave the possibility to opt-out from the loop limit + import std.process : environment; + bool no_loop_limit = environment.get("DUB_NO_RESOLVE_LIMIT") !is null; while (true) { + assert(no_loop_limit || loop_counter++ < 1_000_000, + "The dependency resolution process is taking too long. The" + ~ " dependency graph is likely hitting a pathological case in" + ~ " the resolution algorithm. Please file a bug report at" + ~ " https://github.com/dlang/dub/issues and mention the package" + ~ " recipe that reproduces this error."); + // check if the current combination of configurations works out visited = null; - string error; + ConflictError error; auto conflict_index = validateConfigs(root, error); - if (first_error is null) first_error = error; + if (first_error.isNull) first_error = error; // print out current iteration state logDebug("Interation (ci=%s) %s", conflict_index, { @@ -237,7 +250,7 @@ else break; } if (config_indices.all!"a==0") { - if (throw_on_failure) throw new Exception("Could not find a valid dependency tree configuration: "~first_error); + if (throw_on_failure) throw new Exception(format("Could not find a valid dependency tree configuration: %s", first_error.get)); else return null; } } @@ -271,6 +284,36 @@ if (p !in required) configs.remove(p); } + + private struct ConflictError { + enum Kind { + none, + noRootMatch, + childMismatch, + invalidDependency + } + + Kind kind; + TreeNode parent; + TreeNodes child; + CONFIG config; + + string toString() + const { + final switch (kind) { + case Kind.none: return "no error"; + case Kind.noRootMatch: + return "No match for dependency %s %s of %s" + .format(child.pack, child.configs, parent.pack); + case Kind.childMismatch: + return "Dependency %s -> %s %s mismatches with selected version %s" + .format(parent.pack, child.pack, child.configs, config); + case Kind.invalidDependency: + return "Package %s contains invalid dependency %s (no version candidates)" + .format(parent.pack, child.pack); + } + } + } } enum DependencyType { @@ -281,7 +324,7 @@ private string basePackage(string p) { - auto idx = indexOf(p, ":"); + auto idx = indexOf(p, ':'); if (idx < 0) return p; return p[0 .. idx]; } diff --git a/source/dub/description.d b/source/dub/description.d index 9f519cb..c9c347c 100644 --- a/source/dub/description.d +++ b/source/dub/description.d @@ -44,10 +44,7 @@ foreach (ref p; packages) if (p.name == name) { - static if (__VERSION__ > 2065) - return p; - else - return *cast(inout(PackageDescription)*)&p; + return p; } throw new Exception("Package '"~name~"' not found in dependency tree."); } diff --git a/source/dub/dub.d b/source/dub/dub.d index 3e48d90..c5f0909 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -60,8 +60,14 @@ } /// The URL to the official package registry. -enum defaultRegistryURL = "http://code.dlang.org/"; -enum fallbackRegistryURL = "https://code-mirror.dlang.io/"; +enum defaultRegistryURL = "https://code.dlang.org/"; +enum fallbackRegistryURLs = [ + // fallback in case of HTTPS problems + "http://code.dlang.org/", + "https://code-mirror.dlang.io/", + "https://code-mirror2.dlang.io/", + "https://dub-registry.herokuapp.com/", +]; /** Returns a default list of package suppliers. @@ -76,7 +82,7 @@ return [ new FallbackPackageSupplier( new RegistryPackageSupplier(URL(defaultRegistryURL)), - new RegistryPackageSupplier(URL(fallbackRegistryURL)) + fallbackRegistryURLs.map!(x => cast(PackageSupplier) new RegistryPackageSupplier(URL(x))).array ) ]; } @@ -93,12 +99,12 @@ bool m_dryRun = false; PackageManager m_packageManager; PackageSupplier[] m_packageSuppliers; - Path m_rootPath; + NativePath m_rootPath; SpecialDirs m_dirs; DubConfig m_config; - Path m_projectPath; + NativePath m_projectPath; Project m_project; - Path m_overrideSearchPath; + NativePath m_overrideSearchPath; string m_defaultCompiler; } @@ -127,8 +133,8 @@ this(string root_path = ".", PackageSupplier[] additional_package_suppliers = null, SkipPackageSuppliers skip_registry = SkipPackageSuppliers.none) { - m_rootPath = Path(root_path); - if (!m_rootPath.absolute) m_rootPath = Path(getcwd()) ~ m_rootPath; + m_rootPath = NativePath(root_path); + if (!m_rootPath.absolute) m_rootPath = NativePath(getcwd()) ~ m_rootPath; init(); @@ -136,7 +142,15 @@ if (skip_registry < SkipPackageSuppliers.all) { - ps ~= (environment.get("DUB_REGISTRY", null).split(";") ~ m_config.registryURLs) + ps ~= environment.get("DUB_REGISTRY", null) + .splitter(";") + .map!(url => cast(PackageSupplier)new RegistryPackageSupplier(URL(url))) + .array; + } + + if (skip_registry < SkipPackageSuppliers.configured) + { + ps ~= m_config.registryURLs .map!(url => cast(PackageSupplier)new RegistryPackageSupplier(URL(url))) .array; } @@ -152,16 +166,16 @@ unittest { scope (exit) environment.remove("DUB_REGISTRY"); - auto dub = new Dub(".", null, SkipPackageSuppliers.standard); + auto dub = new Dub(".", null, SkipPackageSuppliers.configured); assert(dub.m_packageSuppliers.length == 0); environment["DUB_REGISTRY"] = "http://example.com/"; - dub = new Dub(".", null, SkipPackageSuppliers.standard); + dub = new Dub(".", null, SkipPackageSuppliers.configured); logInfo("%s", dub.m_packageSuppliers); assert(dub.m_packageSuppliers.length == 1); environment["DUB_REGISTRY"] = "http://example.com/;http://foo.com/"; - dub = new Dub(".", null, SkipPackageSuppliers.standard); + dub = new Dub(".", null, SkipPackageSuppliers.configured); assert(dub.m_packageSuppliers.length == 2); - dub = new Dub(".", [new RegistryPackageSupplier(URL("http://bar.com/"))], SkipPackageSuppliers.standard); + dub = new Dub(".", [new RegistryPackageSupplier(URL("http://bar.com/"))], SkipPackageSuppliers.configured); assert(dub.m_packageSuppliers.length == 3); } @@ -171,11 +185,11 @@ This constructor corresponds to the "--bare" option of the command line interface. Use */ - this(Path override_path) + this(NativePath override_path) { init(); m_overrideSearchPath = override_path; - m_packageManager = new PackageManager(Path(), Path(), false); + m_packageManager = new PackageManager(NativePath(), NativePath(), false); updatePackageSearchPath(); } @@ -183,19 +197,19 @@ { import std.file : tempDir; version(Windows) { - m_dirs.systemSettings = Path(environment.get("ProgramData")) ~ "dub/"; - m_dirs.userSettings = Path(environment.get("APPDATA")) ~ "dub/"; + m_dirs.systemSettings = NativePath(environment.get("ProgramData")) ~ "dub/"; + m_dirs.userSettings = NativePath(environment.get("APPDATA")) ~ "dub/"; } else version(Posix){ - m_dirs.systemSettings = Path("/var/lib/dub/"); - m_dirs.userSettings = Path(environment.get("HOME")) ~ ".dub/"; + m_dirs.systemSettings = NativePath("/var/lib/dub/"); + m_dirs.userSettings = NativePath(environment.get("HOME")) ~ ".dub/"; if (!m_dirs.userSettings.absolute) - m_dirs.userSettings = Path(getcwd()) ~ m_dirs.userSettings; + m_dirs.userSettings = NativePath(getcwd()) ~ m_dirs.userSettings; } - m_dirs.temp = Path(tempDir); + m_dirs.temp = NativePath(tempDir); m_config = new DubConfig(jsonFromFile(m_dirs.systemSettings ~ "settings.json", true), m_config); - m_config = new DubConfig(jsonFromFile(Path(thisExePath).parentPath ~ "../etc/dub/settings.json", true), m_config); + m_config = new DubConfig(jsonFromFile(NativePath(thisExePath).parentPath ~ "../etc/dub/settings.json", true), m_config); m_config = new DubConfig(jsonFromFile(m_dirs.userSettings ~ "settings.json", true), m_config); determineDefaultCompiler(); @@ -205,19 +219,19 @@ /** Returns the root path (usually the current working directory). */ - @property Path rootPath() const { return m_rootPath; } + @property NativePath rootPath() const { return m_rootPath; } /// ditto - @property void rootPath(Path root_path) + @property void rootPath(NativePath root_path) { m_rootPath = root_path; - if (!m_rootPath.absolute) m_rootPath = Path(getcwd()) ~ m_rootPath; + if (!m_rootPath.absolute) m_rootPath = NativePath(getcwd()) ~ m_rootPath; } /// Returns the name listed in the dub.json of the current /// application. @property string projectName() const { return m_project.name; } - @property Path projectPath() const { return m_projectPath; } + @property NativePath projectPath() const { return m_projectPath; } @property string[] configurations() const { return m_project.configurations; } @@ -244,7 +258,7 @@ } /// Loads the package from the specified path as the main project package. - void loadPackage(Path path) + void loadPackage(NativePath path) { m_projectPath = path; updatePackageSearchPath(); @@ -289,7 +303,7 @@ The script above can be invoked with "dub --single test.d". */ - void loadSingleFilePackage(Path path) + void loadSingleFilePackage(NativePath path) { import dub.recipe.io : parsePackageRecipe; import std.file : mkdirRecurse, readText; @@ -342,15 +356,15 @@ /// ditto void loadSingleFilePackage(string path) { - loadSingleFilePackage(Path(path)); + loadSingleFilePackage(NativePath(path)); } /** Disables the default search paths and only searches a specific directory for packages. */ - void overrideSearchPath(Path path) + void overrideSearchPath(NativePath path) { - if (!path.absolute) path = Path(getcwd()) ~ path; + if (!path.absolute) path = NativePath(getcwd()) ~ path; m_overrideSearchPath = path; updatePackageSearchPath(); } @@ -472,7 +486,7 @@ if ((options & UpgradeOptions.select) && p != m_project.rootPackage.name) { if (ver.path.empty) m_project.selections.selectVersion(p, ver.version_); else { - Path relpath = ver.path; + NativePath relpath = ver.path; if (relpath.absolute) relpath = relpath.relativeTo(m_project.rootPackage.path); m_project.selections.selectVersion(p, relpath); } @@ -500,13 +514,13 @@ Throws an exception, if unittests failed. */ - void testProject(GeneratorSettings settings, string config, Path custom_main_file) + void testProject(GeneratorSettings settings, string config, NativePath custom_main_file) { - if (custom_main_file.length && !custom_main_file.absolute) custom_main_file = getWorkingDirectory() ~ custom_main_file; + if (!custom_main_file.empty && !custom_main_file.absolute) custom_main_file = getWorkingDirectory() ~ custom_main_file; if (config.length == 0) { // if a custom main file was given, favor the first library configuration, so that it can be applied - if (custom_main_file.length) config = m_project.getDefaultConfiguration(settings.platform, false); + if (!custom_main_file.empty) config = m_project.getDefaultConfiguration(settings.platform, false); // else look for a "unittest" configuration if (!config.length && m_project.rootPackage.configurations.canFind("unittest")) config = "unittest"; // if not found, fall back to the first "library" configuration @@ -551,7 +565,7 @@ if (!mainfil.length) mainfil = m_project.rootPackage.recipe.buildSettings.mainSourceFile; string custommodname; - if (custom_main_file.length) { + if (!custom_main_file.empty) { import std.path; tcinfo.sourceFiles[""] ~= custom_main_file.relativeTo(m_project.rootPackage.path).toNativeString(); tcinfo.importPaths[""] ~= custom_main_file.parentPath.toNativeString(); @@ -562,8 +576,8 @@ string[] import_modules; foreach (file; lbuildsettings.sourceFiles) { if (file.endsWith(".d")) { - auto fname = Path(file).head.toString(); - if (Path(file).relativeTo(m_project.rootPackage.path) == Path(mainfil)) { + auto fname = NativePath(file).head.toString(); + if (NativePath(file).relativeTo(m_project.rootPackage.path) == NativePath(mainfil)) { logWarn("Excluding main source file %s from test.", mainfil); tcinfo.excludedSourceFiles[""] ~= mainfil; continue; @@ -572,12 +586,12 @@ logWarn("Excluding package.d file from test due to https://issues.dlang.org/show_bug.cgi?id=11847"); continue; } - import_modules ~= dub.internal.utils.determineModuleName(lbuildsettings, Path(file), m_project.rootPackage.path); + import_modules ~= dub.internal.utils.determineModuleName(lbuildsettings, NativePath(file), m_project.rootPackage.path); } } // generate main file - Path mainfile = getTempFile("dub_test_root", ".d"); + NativePath mainfile = getTempFile("dub_test_root", ".d"); tcinfo.sourceFiles[""] ~= mainfile.toNativeString(); tcinfo.mainSourceFile = mainfile.toNativeString(); if (!m_dryRun) { @@ -651,7 +665,7 @@ } /// Cleans intermediate/cache files of the given package - void cleanPackage(Path path) + void cleanPackage(NativePath path) { logInfo("Cleaning package at %s...", path.toNativeString()); enforce(!Package.findPackageFile(path).empty, "No package found.", path.toNativeString()); @@ -682,7 +696,7 @@ enforce(pinfo.type != Json.Type.undefined, "No package "~packageId~" was found matching the dependency "~dep.toString()); string ver = pinfo["version"].get!string; - Path placement; + NativePath placement; final switch (location) { case PlacementLocation.local: placement = m_rootPath; break; case PlacementLocation.user: placement = m_dirs.userSettings ~ "packages/"; break; @@ -726,7 +740,7 @@ clean_package_version = clean_package_version.replace("+", "_"); // + has special meaning for Optlink if (!placement.existsFile()) mkdirRecurse(placement.toNativeString()); - Path dstpath = placement ~ (packageId ~ "-" ~ clean_package_version); + NativePath dstpath = placement ~ (packageId ~ "-" ~ clean_package_version); if (!dstpath.existsFile()) mkdirRecurse(dstpath.toNativeString()); @@ -1022,7 +1036,7 @@ recipe_callback = Optional callback that can be used to customize the recipe before it gets written. */ - void createEmptyPackage(Path path, string[] deps, string type, + void createEmptyPackage(NativePath path, string[] deps, string type, PackageFormat format = PackageFormat.sdl, scope void delegate(ref PackageRecipe, ref PackageFormat) recipe_callback = null) { @@ -1076,13 +1090,13 @@ } auto srcfile = m_project.rootPackage.recipePath; - auto srcext = srcfile[$-1].toString().extension; + auto srcext = srcfile.head.toString().extension; if (srcext == "."~destination_file_ext) { logInfo("Package format is already %s.", destination_file_ext); return; } - writePackageRecipe(srcfile[0 .. $-1] ~ ("dub."~destination_file_ext), m_project.rootPackage.rawRecipe); + writePackageRecipe(srcfile.parentPath ~ ("dub."~destination_file_ext), m_project.rootPackage.rawRecipe); removeFile(srcfile); } @@ -1151,16 +1165,16 @@ private void updatePackageSearchPath() { - if (m_overrideSearchPath.length) { + if (!m_overrideSearchPath.empty) { m_packageManager.disableDefaultSearchPaths = true; m_packageManager.searchPath = [m_overrideSearchPath]; } else { auto p = environment.get("DUBPATH"); - Path[] paths; + NativePath[] paths; version(Windows) enum pathsep = ";"; else enum pathsep = ":"; - if (p.length) paths ~= p.split(pathsep).map!(p => Path(p))().array(); + if (p.length) paths ~= p.split(pathsep).map!(p => NativePath(p))().array(); m_packageManager.disableDefaultSearchPaths = false; m_packageManager.searchPath = paths; } @@ -1168,24 +1182,63 @@ private void determineDefaultCompiler() { - import std.range : front; + import std.file : thisExePath; + import std.path : buildPath, dirName, expandTilde, isAbsolute, isDirSeparator; import std.process : environment; + import std.range : front; + import std.regex : ctRegex, matchFirst; - m_defaultCompiler = m_config.defaultCompiler; - if (m_defaultCompiler.length) return; + m_defaultCompiler = m_config.defaultCompiler.expandTilde; + if (m_defaultCompiler.length && m_defaultCompiler.isAbsolute) + return; + + auto dubPrefix = m_defaultCompiler.matchFirst(ctRegex!(`^\$DUB_BINARY_PATH`)); + if(!dubPrefix.empty) + { + m_defaultCompiler = thisExePath().dirName() ~ dubPrefix.post; + return; + } + + if (!find!isDirSeparator(m_defaultCompiler).empty) + throw new Exception("defaultCompiler specified in a DUB config file cannot use an unqualified relative path:\n\n" ~ m_defaultCompiler ~ + "\n\nUse \"$DUB_BINARY_PATH/../path/you/want\" instead."); version (Windows) enum sep = ";", exe = ".exe"; version (Posix) enum sep = ":", exe = ""; auto compilers = ["dmd", "gdc", "gdmd", "ldc2", "ldmd2"]; + // If a compiler name is specified, look for it next to dub. + // Otherwise, look for any of the common compilers adjacent to dub. + if (m_defaultCompiler.length) + { + string compilerPath = buildPath(thisExePath().dirName(), m_defaultCompiler ~ exe); + if (existsFile(compilerPath)) + { + m_defaultCompiler = compilerPath; + return; + } + } + else + { + auto nextFound = compilers.find!(bin => existsFile(buildPath(thisExePath().dirName(), bin ~ exe))); + if (!nextFound.empty) + { + m_defaultCompiler = buildPath(thisExePath().dirName(), nextFound.front ~ exe); + return; + } + } - auto paths = environment.get("PATH", "").splitter(sep).map!Path; + // If nothing found next to dub, search the user's PATH, starting + // with the compiler name from their DUB config file, if specified. + if (m_defaultCompiler.length) + compilers = m_defaultCompiler ~ compilers; + auto paths = environment.get("PATH", "").splitter(sep).map!NativePath; auto res = compilers.find!(bin => paths.canFind!(p => existsFile(p ~ (bin~exe)))); m_defaultCompiler = res.empty ? compilers[0] : res.front; } - private Path makeAbsolute(Path p) const { return p.absolute ? p : m_rootPath ~ p; } - private Path makeAbsolute(string p) const { return makeAbsolute(Path(p)); } + private NativePath makeAbsolute(NativePath p) const { return p.absolute ? p : m_rootPath ~ p; } + private NativePath makeAbsolute(string p) const { return makeAbsolute(NativePath(p)); } } @@ -1214,9 +1267,10 @@ /// Determines which of the default package suppliers are queried for packages. enum SkipPackageSuppliers { - none, /// Uses all configured package suppliers. - standard, /// Does not use the default package suppliers (`defaultPackageSuppliers`). - all /// Uses only manually specified package suppliers. + none, /// Uses all configured package suppliers. + standard, /// Does not use the default package suppliers (`defaultPackageSuppliers`). + configured, /// Does not use default suppliers or suppliers configured in DUB's configuration file + all /// Uses only manually specified package suppliers. } private class DependencyVersionResolver : DependencyResolver!(Dependency, Dependency) { @@ -1228,6 +1282,8 @@ SelectedVersions m_selectedVersions; Package m_rootPackage; bool[string] m_packagesToUpgrade; + Package[PackageDependency] m_packages; + TreeNodes[][TreeNode] m_children; } @@ -1319,6 +1375,15 @@ protected override TreeNodes[] getChildren(TreeNode node) { + if (auto pc = node in m_children) + return *pc; + auto ret = getChildrenRaw(node); + m_children[node] = ret; + return ret; + } + + private final TreeNodes[] getChildrenRaw(TreeNode node) + { import std.array : appender; auto ret = appender!(TreeNodes[]); auto pack = getPackage(node.pack, node.config); @@ -1329,7 +1394,7 @@ } auto basepack = pack.basePackage; - foreach (d; pack.getAllDependencies()) { + foreach (d; pack.getAllDependenciesRange()) { auto dbasename = getBasePackageName(d.name); // detect dependencies to the root package (or sub packages thereof) @@ -1381,6 +1446,16 @@ private Package getPackage(string name, Dependency dep) { + auto key = PackageDependency(name, dep); + if (auto pp = key in m_packages) + return *pp; + auto p = getPackageRaw(name, dep); + m_packages[key] = p; + return p; + } + + private Package getPackageRaw(string name, Dependency dep) + { auto basename = getBasePackageName(name); // for sub packages, first try to get them from the base package @@ -1476,9 +1551,9 @@ } private struct SpecialDirs { - Path temp; - Path userSettings; - Path systemSettings; + NativePath temp; + NativePath userSettings; + NativePath systemSettings; } private class DubConfig { diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index c23ea9d..8e0a171 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -33,8 +33,8 @@ class BuildGenerator : ProjectGenerator { private { PackageManager m_packageMan; - Path[] m_temporaryFiles; - Path m_targetExecutablePath; + NativePath[] m_temporaryFiles; + NativePath m_targetExecutablePath; } this(Project project) @@ -52,7 +52,7 @@ bool any_cached = false; - Path[string] target_paths; + NativePath[string] target_paths; bool[string] visited; void buildTargetRec(string target) @@ -65,7 +65,7 @@ foreach (dep; ti.dependencies) buildTargetRec(dep); - Path[] additional_dep_files; + NativePath[] additional_dep_files; auto bs = ti.buildSettings.dup; foreach (ldep; ti.linkDependencies) { auto dbs = targets[ldep].buildSettings; @@ -75,7 +75,7 @@ additional_dep_files ~= target_paths[ldep]; } } - Path tpath; + NativePath tpath; if (buildTarget(settings, bs, ti.pack, ti.config, ti.packages, additional_dep_files, tpath)) any_cached = true; target_paths[target] = tpath; @@ -86,7 +86,7 @@ if (settings.rdmd || root_ti.buildSettings.targetType == TargetType.staticLibrary) { // RDMD always builds everything at once and static libraries don't need their // dependencies to be built - Path tpath; + NativePath tpath; buildTarget(settings, root_ti.buildSettings.dup, m_project.rootPackage, root_ti.config, root_ti.packages, null, tpath); } else { buildTargetRec(m_project.rootPackage.name); @@ -102,8 +102,8 @@ // run the generated executable auto buildsettings = targets[m_project.rootPackage.name].buildSettings.dup; if (settings.run && !(buildsettings.options & BuildOption.syntaxOnly)) { - Path exe_file_path; - if (!m_targetExecutablePath.length) + NativePath exe_file_path; + if (m_targetExecutablePath.empty) exe_file_path = getTargetPath(buildsettings, settings); else exe_file_path = m_targetExecutablePath ~ settings.compiler.getTargetFileName(buildsettings, settings.platform); @@ -111,15 +111,15 @@ } } - private bool buildTarget(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, in Package[] packages, in Path[] additional_dep_files, out Path target_path) + private bool buildTarget(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, in Package[] packages, in NativePath[] additional_dep_files, out NativePath target_path) { - auto cwd = Path(getcwd()); + auto cwd = NativePath(getcwd()); bool generate_binary = !(buildsettings.options & BuildOption.syntaxOnly); auto build_id = computeBuildID(config, buildsettings, settings); // make all paths relative to shrink the command line - string makeRelative(string path) { return shrinkPath(Path(path), cwd); } + string makeRelative(string path) { return shrinkPath(NativePath(path), cwd); } foreach (ref f; buildsettings.sourceFiles) f = makeRelative(f); foreach (ref p; buildsettings.importPaths) p = makeRelative(p); foreach (ref p; buildsettings.stringImportPaths) p = makeRelative(p); @@ -149,11 +149,11 @@ } private bool performCachedBuild(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config, - string build_id, in Package[] packages, in Path[] additional_dep_files, out Path target_binary_path) + string build_id, in Package[] packages, in NativePath[] additional_dep_files, out NativePath target_binary_path) { - auto cwd = Path(getcwd()); + auto cwd = NativePath(getcwd()); - Path target_path; + NativePath target_path; if (settings.tempBuild) { string packageName = pack.basePackage is null ? pack.name : pack.basePackage.name; m_targetExecutablePath = target_path = getTempDir() ~ format(".dub/build/%s-%s/%s/", packageName, pack.version_, build_id); @@ -198,14 +198,14 @@ return false; } - private void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out Path target_path) + private void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out NativePath target_path) { - auto cwd = Path(getcwd()); + auto cwd = NativePath(getcwd()); //Added check for existence of [AppNameInPackagejson].d //If exists, use that as the starting file. - Path mainsrc; + NativePath mainsrc; if (buildsettings.mainSourceFile.length) { - mainsrc = Path(buildsettings.mainSourceFile); + mainsrc = NativePath(buildsettings.mainSourceFile); if (!mainsrc.absolute) mainsrc = pack.path ~ mainsrc; } else { mainsrc = getMainSourceFile(pack); @@ -223,7 +223,7 @@ // or with "/" instead of "\" bool tmp_target = false; if (generate_binary) { - if (settings.tempBuild || (settings.run && !isWritableDir(Path(buildsettings.targetPath), true))) { + if (settings.tempBuild || (settings.run && !isWritableDir(NativePath(buildsettings.targetPath), true))) { import std.random; auto rnd = to!string(uniform(uint.min, uint.max)) ~ "-"; auto tmpdir = getTempDir()~".rdmd/source/"; @@ -259,20 +259,20 @@ if (tmp_target) { m_temporaryFiles ~= target_path; foreach (f; buildsettings.copyFiles) - m_temporaryFiles ~= Path(buildsettings.targetPath).parentPath ~ Path(f).head; + m_temporaryFiles ~= NativePath(buildsettings.targetPath).parentPath ~ NativePath(f).head; } } - private void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out Path target_path) + private void performDirectBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out NativePath target_path) { - auto cwd = Path(getcwd()); + auto cwd = NativePath(getcwd()); auto generate_binary = !(buildsettings.options & BuildOption.syntaxOnly); auto is_static_library = buildsettings.targetType == TargetType.staticLibrary || buildsettings.targetType == TargetType.library; // make file paths relative to shrink the command line foreach (ref f; buildsettings.sourceFiles) { - auto fp = Path(f); + auto fp = NativePath(f); if( fp.absolute ) fp = fp.relativeTo(cwd); f = fp.toNativeString(); } @@ -281,7 +281,7 @@ // make all target/import paths relative string makeRelative(string path) { - auto p = Path(path); + auto p = NativePath(path); // storing in a separate temprary to work around #601 auto prel = p.absolute ? p.relativeTo(cwd) : p; return prel.toNativeString(); @@ -292,7 +292,7 @@ bool is_temp_target = false; if (generate_binary) { - if (settings.tempBuild || (settings.run && !isWritableDir(Path(buildsettings.targetPath), true))) { + if (settings.tempBuild || (settings.run && !isWritableDir(NativePath(buildsettings.targetPath), true))) { import std.random; auto rnd = to!string(uniform(uint.min, uint.max)); auto tmppath = getTempDir()~("dub/"~rnd~"/"); @@ -313,7 +313,7 @@ if (is_temp_target) { m_temporaryFiles ~= target_path; foreach (f; buildsettings.copyFiles) - m_temporaryFiles ~= Path(buildsettings.targetPath).parentPath ~ Path(f).head; + m_temporaryFiles ~= NativePath(buildsettings.targetPath).parentPath ~ NativePath(f).head; } } @@ -347,17 +347,17 @@ settings.platform.compiler, settings.platform.frontendVersion, hashstr); } - private void copyTargetFile(Path build_path, BuildSettings buildsettings, GeneratorSettings settings) + private void copyTargetFile(NativePath build_path, BuildSettings buildsettings, GeneratorSettings settings) { auto filename = settings.compiler.getTargetFileName(buildsettings, settings.platform); auto src = build_path ~ filename; logDiagnostic("Copying target from %s to %s", src.toNativeString(), buildsettings.targetPath); - if (!existsFile(Path(buildsettings.targetPath))) + if (!existsFile(NativePath(buildsettings.targetPath))) mkdirRecurse(buildsettings.targetPath); - hardLinkFile(src, Path(buildsettings.targetPath) ~ filename, true); + hardLinkFile(src, NativePath(buildsettings.targetPath) ~ filename, true); } - private bool isUpToDate(Path target_path, BuildSettings buildsettings, GeneratorSettings settings, in Package main_pack, in Package[] packages, in Path[] additional_dep_files) + private bool isUpToDate(NativePath target_path, BuildSettings buildsettings, GeneratorSettings settings, in Package main_pack, in Package[] packages, in NativePath[] additional_dep_files) { import std.datetime; @@ -374,7 +374,7 @@ allfiles ~= buildsettings.stringImportFiles; // TODO: add library files foreach (p; packages) - allfiles ~= (p.recipePath != Path.init ? p : p.basePackage).recipePath.toNativeString(); + allfiles ~= (p.recipePath != NativePath.init ? p : p.basePackage).recipePath.toNativeString(); foreach (f; additional_dep_files) allfiles ~= f.toNativeString(); if (main_pack is m_project.rootPackage && m_project.rootPackage.getAllDependencies().length > 0) allfiles ~= (main_pack.path ~ SelectedVersions.defaultFile).toNativeString(); @@ -401,13 +401,17 @@ static string pathToObjName(string path) { - import std.path : buildNormalizedPath, dirSeparator, stripDrive; - return stripDrive(buildNormalizedPath(getcwd(), path~objSuffix))[1..$].replace(dirSeparator, "."); + import std.digest.crc : crc32Of; + import std.path : buildNormalizedPath, dirSeparator, relativePath, stripDrive; + if (path.endsWith(".d")) path = path[0 .. $-2]; + auto ret = buildNormalizedPath(getcwd(), path).replace(dirSeparator, "."); + auto idx = ret.lastIndexOf('.'); + return idx < 0 ? ret ~ objSuffix : format("%s_%(%02x%)%s", ret[idx+1 .. $], crc32Of(ret[0 .. idx]), objSuffix); } /// 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; + NativePath tempobj = NativePath(bs.targetPath)~objName; string objPath = tempobj.toNativeString(); bs.libs = null; bs.lflags = null; @@ -424,7 +428,7 @@ auto generate_binary = !(buildsettings.options & BuildOption.syntaxOnly); auto is_static_library = buildsettings.targetType == TargetType.staticLibrary || buildsettings.targetType == TargetType.library; - Path target_file; + NativePath target_file; scope (failure) { logDiagnostic("FAIL %s %s %s" , buildsettings.targetPath, buildsettings.targetName, buildsettings.targetType); auto tpath = getTargetPath(buildsettings, settings); @@ -473,7 +477,7 @@ } else { // determine path for the temporary object file string tempobjname = buildsettings.targetName ~ objSuffix; - Path tempobj = Path(buildsettings.targetPath) ~ tempobjname; + NativePath tempobj = NativePath(buildsettings.targetPath) ~ tempobjname; // setup linker command line auto lbuildsettings = buildsettings; @@ -498,13 +502,13 @@ } } - private void runTarget(Path exe_file_path, in BuildSettings buildsettings, string[] run_args, GeneratorSettings settings) + private void runTarget(NativePath exe_file_path, in BuildSettings buildsettings, string[] run_args, GeneratorSettings settings) { if (buildsettings.targetType == TargetType.executable) { - auto cwd = Path(getcwd()); + auto cwd = NativePath(getcwd()); auto runcwd = cwd; if (buildsettings.workingDirectory.length) { - runcwd = Path(buildsettings.workingDirectory); + runcwd = NativePath(buildsettings.workingDirectory); if (!runcwd.absolute) runcwd = cwd ~ runcwd; logDiagnostic("Switching to %s", runcwd.toNativeString()); chdir(runcwd.toNativeString()); @@ -548,7 +552,7 @@ } } -private Path getMainSourceFile(in Package prj) +private NativePath getMainSourceFile(in Package prj) { foreach (f; ["source/app.d", "src/app.d", "source/"~prj.name~".d", "src/"~prj.name~".d"]) if (existsFile(prj.path ~ f)) @@ -556,12 +560,12 @@ return prj.path ~ "source/app.d"; } -private Path getTargetPath(in ref BuildSettings bs, in ref GeneratorSettings settings) +private NativePath getTargetPath(in ref BuildSettings bs, in ref GeneratorSettings settings) { - return Path(bs.targetPath) ~ settings.compiler.getTargetFileName(bs, settings.platform); + return NativePath(bs.targetPath) ~ settings.compiler.getTargetFileName(bs, settings.platform); } -private string shrinkPath(Path path, Path base) +private string shrinkPath(NativePath path, NativePath base) { auto orig = path.toNativeString(); if (!path.absolute) return orig; @@ -570,10 +574,10 @@ } unittest { - assert(shrinkPath(Path("/foo/bar/baz"), Path("/foo")) == Path("bar/baz").toNativeString()); - assert(shrinkPath(Path("/foo/bar/baz"), Path("/foo/baz")) == Path("../bar/baz").toNativeString()); - assert(shrinkPath(Path("/foo/bar/baz"), Path("/bar/")) == Path("/foo/bar/baz").toNativeString()); - assert(shrinkPath(Path("/foo/bar/baz"), Path("/bar/baz")) == Path("/foo/bar/baz").toNativeString()); + assert(shrinkPath(NativePath("/foo/bar/baz"), NativePath("/foo")) == NativePath("bar/baz").toNativeString()); + assert(shrinkPath(NativePath("/foo/bar/baz"), NativePath("/foo/baz")) == NativePath("../bar/baz").toNativeString()); + assert(shrinkPath(NativePath("/foo/bar/baz"), NativePath("/bar/")) == NativePath("/foo/bar/baz").toNativeString()); + assert(shrinkPath(NativePath("/foo/bar/baz"), NativePath("/bar/baz")) == NativePath("/foo/bar/baz").toNativeString()); } unittest { // issue #1235 - pass no library files to compiler command line when building a static lib @@ -585,8 +589,8 @@ else auto libfile = "bar.a"; auto desc = parseJsonString(`{"name": "test", "targetType": "library", "sourceFiles": ["foo.d", "`~libfile~`"]}`); - auto pack = new Package(desc, Path("/tmp/fooproject")); - auto pman = new PackageManager(Path("/tmp/foo/"), Path("/tmp/foo/"), false); + auto pack = new Package(desc, NativePath("/tmp/fooproject")); + auto pman = new PackageManager(NativePath("/tmp/foo/"), NativePath("/tmp/foo/"), false); auto prj = new Project(pman, pack); final static class TestCompiler : GDCCompiler { diff --git a/source/dub/generators/cmake.d b/source/dub/generators/cmake.d index 269c136..595442d 100644 --- a/source/dub/generators/cmake.d +++ b/source/dub/generators/cmake.d @@ -32,8 +32,8 @@ auto script = appender!(char[]); auto scripts = appender!(string[]); bool[string] visited; - Path projectRoot = m_project.rootPackage.path; - Path cmakeListsPath = projectRoot ~ "CMakeLists.txt"; + NativePath projectRoot = m_project.rootPackage.path; + NativePath cmakeListsPath = projectRoot ~ "CMakeLists.txt"; foreach(name, info; targets) { diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 6154bfb..dfb0670 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -112,7 +112,7 @@ BuildSettings buildsettings; buildsettings.processVars(m_project, pack, pack.getBuildSettings(settings.platform, configs[pack.name]), true); bool generate_binary = !(buildsettings.options & BuildOption.syntaxOnly); - finalizeGeneration(pack, m_project, settings, buildsettings, Path(bs.targetPath), generate_binary); + finalizeGeneration(pack, m_project, settings, buildsettings, NativePath(bs.targetPath), generate_binary); } performPostGenerateActions(settings, targets); @@ -246,7 +246,7 @@ // override string import files (used for up to date checking) foreach (ref f; ti.buildSettings.stringImportFiles) foreach (fi; root_settings.stringImportFiles) - if (f != fi && Path(f).head == Path(fi).head) { + if (f != fi && NativePath(f).head == NativePath(fi).head) { f = fi; } @@ -364,7 +364,7 @@ Runs post-build commands and copies required files to the binary directory. */ private void finalizeGeneration(in Package pack, in Project proj, in GeneratorSettings settings, - in BuildSettings buildsettings, Path target_path, bool generate_binary) + in BuildSettings buildsettings, NativePath target_path, bool generate_binary) { import std.path : globMatch; @@ -378,7 +378,7 @@ mkdirRecurse(buildsettings.targetPath); if (buildsettings.copyFiles.length) { - void copyFolderRec(Path folder, Path dstfolder) + void copyFolderRec(NativePath folder, NativePath dstfolder) { mkdirRecurse(dstfolder.toNativeString()); foreach (de; iterateDirectory(folder.toNativeString())) { @@ -395,9 +395,9 @@ void tryCopyDir(string file) { - auto src = Path(file); + auto src = NativePath(file); if (!src.absolute) src = pack.path ~ src; - auto dst = target_path ~ Path(file).head; + auto dst = target_path ~ NativePath(file).head; if (src == dst) { logDiagnostic("Skipping copy of %s (same source and destination)", file); return; @@ -410,9 +410,9 @@ void tryCopyFile(string file) { - auto src = Path(file); + auto src = NativePath(file); if (!src.absolute) src = pack.path ~ src; - auto dst = target_path ~ Path(file).head; + auto dst = target_path ~ NativePath(file).head; if (src == dst) { logDiagnostic("Skipping copy of %s (same source and destination)", file); return; diff --git a/source/dub/generators/sublimetext.d b/source/dub/generators/sublimetext.d index c22fcb1..57a8012 100644 --- a/source/dub/generators/sublimetext.d +++ b/source/dub/generators/sublimetext.d @@ -11,6 +11,7 @@ import dub.generators.generator; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; +import dub.internal.vibecompat.inet.path; import dub.packagemanager; import dub.project; @@ -84,7 +85,7 @@ string fileRegex; if (buildPlatform.frontendVersion >= 2066 && buildPlatform.compiler == "dmd") - fileRegex = r"^(.+)\(([0-9]+)\,([0-9]+)\)\:() (.*)$"; + fileRegex = r"^(.+)\(([0-9]+)\,([0-9]+)\)\: (.*)$"; else fileRegex = r"^(.+)\(([0-9]+)\)\:() (.*)$"; diff --git a/source/dub/generators/targetdescription.d b/source/dub/generators/targetdescription.d index d2aec6e..a84883c 100644 --- a/source/dub/generators/targetdescription.d +++ b/source/dub/generators/targetdescription.d @@ -52,7 +52,7 @@ foreach (ld; ti.linkDependencies) { auto ltarget = targets[ld]; auto ltbs = ltarget.buildSettings; - auto targetfil = (Path(ltbs.targetPath) ~ settings.compiler.getTargetFileName(ltbs, settings.platform)).toNativeString(); + auto targetfil = (NativePath(ltbs.targetPath) ~ settings.compiler.getTargetFileName(ltbs, settings.platform)).toNativeString(); d.buildSettings.addLinkerFiles(targetfil); } diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index e47da77..bd1fd95 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -170,7 +170,7 @@ // Add all files auto files = targets[packname].buildSettings; SourceFile[string] sourceFiles; - void addSourceFile(Path file_path, Path structure_path, bool build) + void addSourceFile(NativePath file_path, NativePath structure_path, bool build) { auto key = file_path.toString(); auto sf = sourceFiles.get(key, SourceFile.init); @@ -183,7 +183,7 @@ } void addFile(string s, bool build) { - auto sp = Path(s); + auto sp = NativePath(s); assert(sp.absolute, format("Source path in %s expected to be absolute: %s", packname, s)); //if( !sp.absolute ) sp = pack.path ~ sp; addSourceFile(sp.relativeTo(project_file_dir), determineStructurePath(sp, targets[packname]), build); @@ -203,10 +203,10 @@ // Create folders and files ret.formattedWrite(" ", getPackageFileName(packname)); - Path lastFolder; + typeof(NativePath.init.head)[] lastFolder; foreach(source; sortedSources(sourceFiles.values)) { logDebug("source looking at %s", source.structurePath); - auto cur = source.structurePath[0 .. source.structurePath.length-1]; + auto cur = source.structurePath.parentPath.bySegment.array; if(lastFolder != cur) { size_t same = 0; foreach(idx; 0..min(lastFolder.length, cur.length)) @@ -248,7 +248,7 @@ auto ret = new string[settings.length]; foreach (i; 0 .. settings.length) { // \" is interpreted as an escaped " by cmd.exe, so we need to avoid that - auto p = Path(settings[i]).relativeTo(project_file_dir); + auto p = NativePath(settings[i]).relativeTo(project_file_dir); p.endsWithSlash = false; ret[i] = '"' ~ p.toNativeString() ~ '"'; } @@ -285,7 +285,7 @@ output_type = DynamicLib; output_ext = "dll"; } - auto bin_path = pack == m_project.rootPackage.name ? Path(buildsettings.targetPath) : Path("lib/"); + auto bin_path = pack == m_project.rootPackage.name ? NativePath(buildsettings.targetPath) : NativePath("lib/"); bin_path.endsWithSlash = true; ret.formattedWrite(" %s\n", output_type); ret.formattedWrite(" %s%s.%s\n", bin_path.toNativeString(), buildsettings.targetName, output_ext); @@ -315,10 +315,10 @@ // compute directory for intermediate files (need dummy/ because of how -op determines the resulting path) size_t ndummy = 0; foreach (f; buildsettings.sourceFiles) { - auto rpath = Path(f).relativeTo(project_file_dir); + auto rpath = NativePath(f).relativeTo(project_file_dir); size_t nd = 0; - foreach (i; 0 .. rpath.length) - if (rpath[i] == "..") + foreach (s; rpath.bySegment) + if (s == "..") nd++; if (nd > ndummy) ndummy = nd; } @@ -406,7 +406,7 @@ ret.put(" \n"); ret.put(" \n"); ret.put(" \n"); - auto wdir = Path(buildsettings.workingDirectory); + auto wdir = NativePath(buildsettings.workingDirectory); if (!wdir.absolute) wdir = m_project.rootPackage.path ~ wdir; ret.formattedWrite(" %s\n", wdir.relativeTo(project_file_dir).toNativeString()); @@ -441,8 +441,8 @@ else return getPackageFileName(m_project.rootPackage.name) ~ ".sln"; } - Path projFileName(string pack) const { - auto basepath = Path(".dub/"); + NativePath projFileName(string pack) const { + auto basepath = NativePath(".dub/"); version(DUBBING) return basepath ~ (getPackageFileName(pack) ~ ".dubbed.visualdproj"); else return basepath ~ (getPackageFileName(pack) ~ ".visualdproj"); } @@ -450,8 +450,8 @@ // TODO: nice folders private struct SourceFile { - Path structurePath; - Path filePath; + NativePath structurePath; + NativePath filePath; bool build; hash_t toHash() const nothrow @trusted { return structurePath.toHash() ^ filePath.toHash() ^ (build * 0x1f3e7b2c); } @@ -460,13 +460,18 @@ private final static int sortOrder(ref const SourceFile a, ref const SourceFile b) { assert(!a.structurePath.empty); assert(!b.structurePath.empty); - auto as = a.structurePath; - auto bs = b.structurePath; + static if (is(typeof(a.structurePath.nodes))) { // vibe.d < 0.8.2 + auto as = a.structurePath.nodes; + auto bs = b.structurePath.nodes; + } else { + auto as = a.structurePath.bySegment.array; + auto bs = b.structurePath.bySegment.array; + } // Check for different folders, compare folders only (omit last one). for(uint idx=0; idx0.7.30"); @@ -139,7 +139,7 @@ }); } -private void initDeimosPackage(Path root_path, ref PackageRecipe p, scope void delegate() pre_write_callback) +private void initDeimosPackage(NativePath root_path, ref PackageRecipe p, scope void delegate() pre_write_callback) { import dub.compilers.buildsettings : TargetType; @@ -153,7 +153,7 @@ createDirectory(root_path ~ "deimos"); } -private void writeGitignore(Path root_path, PackageRecipe p) +private void writeGitignore(NativePath root_path, PackageRecipe p) { write((root_path ~ ".gitignore").toNativeString(), q"{.dub diff --git a/source/dub/internal/sdlang/lexer.d b/source/dub/internal/sdlang/lexer.d index 2295ed7..dd99719 100644 --- a/source/dub/internal/sdlang/lexer.d +++ b/source/dub/internal/sdlang/lexer.d @@ -1,4 +1,4 @@ -// SDLang-D +// SDLang-D // Written in the D programming language. module dub.internal.sdlang.lexer; @@ -1259,8 +1259,7 @@ else { auto timezone = new immutable SimpleTimeZone(offset.get()); - static if (__VERSION__ >= 2067) auto fsecs = dateTimeFrac.fracSecs; - else auto fsecs = FracSec.from!"hnsecs"(dateTimeFrac.fracSecs.total!"hnsecs"); + auto fsecs = dateTimeFrac.fracSecs; mixin(accept!("Value", "SysTime(dateTimeFrac.dateTime, fsecs, timezone)")); } } @@ -1268,9 +1267,8 @@ try { auto timezone = PosixTimeZone.getTimeZone(timezoneStr); - if(timezone) { - static if (__VERSION__ >= 2067) auto fsecs = dateTimeFrac.fracSecs; - else auto fsecs = FracSec.from!"hnsecs"(dateTimeFrac.fracSecs.total!"hnsecs"); + if (timezone) { + auto fsecs = dateTimeFrac.fracSecs; mixin(accept!("Value", "SysTime(dateTimeFrac.dateTime, fsecs, timezone)")); } } @@ -1792,18 +1790,18 @@ testLex( "2013/2/22 -07:53", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53)))) ]); testLex("-2013/2/22 -07:53", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime(-2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53)))) ]); testLex( "2013/2/22 07:53:34", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34)))) ]); - testLex( "2013/2/22 07:53:34.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(123)))) ]); - testLex( "2013/2/22 07:53:34.12", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(120)))) ]); - testLex( "2013/2/22 07:53:34.1", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(100)))) ]); - testLex( "2013/2/22 07:53.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"(123)))) ]); + testLex( "2013/2/22 07:53:34.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), 123.msecs))) ]); + testLex( "2013/2/22 07:53:34.12", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), 120.msecs))) ]); + testLex( "2013/2/22 07:53:34.1", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 34), 100.msecs))) ]); + testLex( "2013/2/22 07:53.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 7, 53, 0), 123.msecs))) ]); testLex( "2013/2/22 34:65", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) + hours(34) + minutes(65) + seconds( 0)))) ]); - testLex( "2013/2/22 34:65:77.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) + hours(34) + minutes(65) + seconds(77), FracSec.from!"msecs"(123)))) ]); - testLex( "2013/2/22 34:65.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) + hours(34) + minutes(65) + seconds( 0), FracSec.from!"msecs"(123)))) ]); + testLex( "2013/2/22 34:65:77.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) + hours(34) + minutes(65) + seconds(77), 123.msecs))) ]); + testLex( "2013/2/22 34:65.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) + hours(34) + minutes(65) + seconds( 0), 123.msecs))) ]); testLex( "2013/2/22 -34:65", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds( 0)))) ]); - testLex( "2013/2/22 -34:65:77.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds(77), FracSec.from!"msecs"(-123)))) ]); - testLex( "2013/2/22 -34:65.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds( 0), FracSec.from!"msecs"(-123)))) ]); + testLex( "2013/2/22 -34:65:77.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds(77), -123.msecs))) ]); + testLex( "2013/2/22 -34:65.123", [ Token(symbol!"Value",loc,Value(DateTimeFrac(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds( 0), -123.msecs))) ]); testLexThrows("2013/2/22 07:53a"); testLexThrows("2013/2/22 07:53f"); @@ -1851,12 +1849,12 @@ testLex( "2013/2/22 07:53:34-GMT+00:00", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), new immutable SimpleTimeZone( hours(0) )))) ]); testLex( "2013/2/22 07:53:34-GMT+02:10", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), new immutable SimpleTimeZone( hours(2)+minutes(10))))) ]); testLex( "2013/2/22 07:53:34-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); - testLex( "2013/2/22 07:53:34.123-GMT+00:00", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(123), new immutable SimpleTimeZone( hours(0) )))) ]); - testLex( "2013/2/22 07:53:34.123-GMT+02:10", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(123), new immutable SimpleTimeZone( hours(2)+minutes(10))))) ]); - testLex( "2013/2/22 07:53:34.123-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(123), new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); - testLex( "2013/2/22 07:53.123-GMT+00:00", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"(123), new immutable SimpleTimeZone( hours(0) )))) ]); - testLex( "2013/2/22 07:53.123-GMT+02:10", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"(123), new immutable SimpleTimeZone( hours(2)+minutes(10))))) ]); - testLex( "2013/2/22 07:53.123-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"(123), new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); + testLex( "2013/2/22 07:53:34.123-GMT+00:00", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), 123.msecs, new immutable SimpleTimeZone( hours(0) )))) ]); + testLex( "2013/2/22 07:53:34.123-GMT+02:10", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), 123.msecs, new immutable SimpleTimeZone( hours(2)+minutes(10))))) ]); + testLex( "2013/2/22 07:53:34.123-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 34), 123.msecs, new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); + testLex( "2013/2/22 07:53.123-GMT+00:00", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), 123.msecs, new immutable SimpleTimeZone( hours(0) )))) ]); + testLex( "2013/2/22 07:53.123-GMT+02:10", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), 123.msecs, new immutable SimpleTimeZone( hours(2)+minutes(10))))) ]); + testLex( "2013/2/22 07:53.123-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 7, 53, 0), 123.msecs, new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); testLex( "2013/2/22 -34:65-GMT-05:30", [ Token(symbol!"Value",loc,Value(SysTime(DateTime( 2013, 2, 22, 0, 0, 0) - hours(34) - minutes(65) - seconds( 0), new immutable SimpleTimeZone(-hours(5)-minutes(30))))) ]); @@ -1871,7 +1869,7 @@ Token testTokenUnknownTimeZone(string tzName) { auto dateTime = DateTime(2013, 2, 22, 7, 53, 0); - auto frac = FracSec.from!"msecs"(0); + auto frac = 0.msecs; return Token( symbol!"Value", loc, Value(DateTimeFracUnknownZone(dateTime,frac,tzName)) ); } testLex("2013/2/22 07:53-GMT+", [ testTokenUnknownTimeZone("GMT+") ]); @@ -1912,13 +1910,13 @@ testLex("2013/2/22 07:53-GMT+00004:003", [ testTokenUnknownTimeZone("GMT+00004:003") ]); // DateTime, with unknown timezone - testLex( "2013/2/22 07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"( 0), "Bogus/Foo")), "2013/2/22 07:53-Bogus/Foo") ]); - testLex("-2013/2/22 07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime(-2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"( 0), "Bogus/Foo"))) ]); - testLex( "2013/2/22 -07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53), FracSec.from!"msecs"( 0), "Bogus/Foo"))) ]); - testLex("-2013/2/22 -07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime(-2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53), FracSec.from!"msecs"( 0), "Bogus/Foo"))) ]); - testLex( "2013/2/22 07:53:34-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"( 0), "Bogus/Foo"))) ]); - testLex( "2013/2/22 07:53:34.123-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 34), FracSec.from!"msecs"(123), "Bogus/Foo"))) ]); - testLex( "2013/2/22 07:53.123-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 0), FracSec.from!"msecs"(123), "Bogus/Foo"))) ]); + testLex( "2013/2/22 07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 0), 0.msecs, "Bogus/Foo")), "2013/2/22 07:53-Bogus/Foo") ]); + testLex("-2013/2/22 07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime(-2013, 2, 22, 7, 53, 0), 0.msecs, "Bogus/Foo"))) ]); + testLex( "2013/2/22 -07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53), 0.msecs, "Bogus/Foo"))) ]); + testLex("-2013/2/22 -07:53-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime(-2013, 2, 22, 0, 0, 0) - hours(7) - minutes(53), 0.msecs, "Bogus/Foo"))) ]); + testLex( "2013/2/22 07:53:34-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 34), 0.msecs, "Bogus/Foo"))) ]); + testLex( "2013/2/22 07:53:34.123-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 34), 123.msecs, "Bogus/Foo"))) ]); + testLex( "2013/2/22 07:53.123-Bogus/Foo", [ Token(symbol!"Value",loc,Value(DateTimeFracUnknownZone(DateTime( 2013, 2, 22, 7, 53, 0), 123.msecs, "Bogus/Foo"))) ]); // Time Span testLex( "12:14:42", [ Token(symbol!"Value",loc,Value( days( 0)+hours(12)+minutes(14)+seconds(42)+msecs( 0))) ]); diff --git a/source/dub/internal/sdlang/token.d b/source/dub/internal/sdlang/token.d index b5f8f4a..a86ae5d 100644 --- a/source/dub/internal/sdlang/token.d +++ b/source/dub/internal/sdlang/token.d @@ -1,4 +1,4 @@ -// SDLang-D +// SDLang-D // Written in the D programming language. module dub.internal.sdlang.token; @@ -23,14 +23,9 @@ struct DateTimeFrac { this(DateTime dt, Duration fs) { this.dateTime = dt; this.fracSecs = fs; } - this(DateTime dt, FracSec fs) { this.dateTime = dt; this.fracSecs = fs.hnsecs.hnsecs; } DateTime dateTime; Duration fracSecs; - deprecated("Use fracSecs instead.") { - @property FracSec fracSec() const { return FracSec.from!"hnsecs"(fracSecs.total!"hnsecs"); } - @property void fracSec(FracSec v) { fracSecs = v.hnsecs.hnsecs; } - } } /++ @@ -47,10 +42,6 @@ { DateTime dateTime; Duration fracSecs; - deprecated("Use fracSecs instead.") { - @property FracSec fracSec() { return FracSec.from!"hnsecs"(fracSecs.total!"hnsecs"); } - @property void fracSec(FracSec v) { fracSecs = v.hnsecs.hnsecs; } - } string timeZone; bool opEquals(const DateTimeFracUnknownZone b) const @@ -247,10 +238,7 @@ void toSDLString(Sink)(SysTime value, ref Sink sink) if(isOutputRange!(Sink,char)) { - static if (__VERSION__ >= 2067) - auto dateTimeFrac = DateTimeFrac(cast(DateTime)value, value.fracSecs); - else - auto dateTimeFrac = DateTimeFrac(cast(DateTime)value, value.fracSec); + auto dateTimeFrac = DateTimeFrac(cast(DateTime)value, value.fracSecs); toSDLString(dateTimeFrac, sink); sink.put("-"); @@ -286,12 +274,7 @@ sink.put("+"); long hours, minutes; - static if (__VERSION__ >= 2066) - offset.split!("hours", "minutes")(hours, minutes); - else { - hours = offset.hours; - minutes = offset.minutes; - } + offset.split!("hours", "minutes")(hours, minutes); sink.put("%.2s".format(hours)); sink.put(":"); @@ -326,14 +309,7 @@ } long hours, minutes, seconds, msecs; - static if (__VERSION__ >= 2066) - value.split!("hours", "minutes", "seconds", "msecs")(hours, minutes, seconds, msecs); - else { - hours = value.hours; - minutes = value.minutes; - seconds = value.seconds; - msecs = value.fracSec.msecs; - } + value.split!("hours", "minutes", "seconds", "msecs")(hours, minutes, seconds, msecs); sink.put("%.2s".format(hours)); sink.put(':'); @@ -500,15 +476,15 @@ assert(Value(DateTimeFrac(DateTime(-2004,10,31, 14,30,15))).toSDLString() == "-2004/10/31 14:30:15"); // DateTimeFrac w/ Frac - assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"(123))).toSDLString() == "2004/10/31 14:30:15.123"); - assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"(120))).toSDLString() == "2004/10/31 14:30:15.120"); - assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"(100))).toSDLString() == "2004/10/31 14:30:15.100"); - assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"( 12))).toSDLString() == "2004/10/31 14:30:15.012"); - assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"( 1))).toSDLString() == "2004/10/31 14:30:15.001"); - assert(Value(DateTimeFrac(DateTime(-2004,10,31, 14,30,15), FracSec.from!"msecs"(123))).toSDLString() == "-2004/10/31 14:30:15.123"); + assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), 123.msecs)).toSDLString() == "2004/10/31 14:30:15.123"); + assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), 120.msecs)).toSDLString() == "2004/10/31 14:30:15.120"); + assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), 100.msecs)).toSDLString() == "2004/10/31 14:30:15.100"); + assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), 12.msecs)).toSDLString() == "2004/10/31 14:30:15.012"); + assert(Value(DateTimeFrac(DateTime(2004,10,31, 14,30,15), 1.msecs)).toSDLString() == "2004/10/31 14:30:15.001"); + assert(Value(DateTimeFrac(DateTime(-2004,10,31, 14,30,15), 123.msecs)).toSDLString() == "-2004/10/31 14:30:15.123"); // DateTimeFracUnknownZone - assert(Value(DateTimeFracUnknownZone(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"(123), "Foo/Bar")).toSDLString() == "2004/10/31 14:30:15.123-Foo/Bar"); + assert(Value(DateTimeFracUnknownZone(DateTime(2004,10,31, 14,30,15), 123.msecs, "Foo/Bar")).toSDLString() == "2004/10/31 14:30:15.123-Foo/Bar"); // SysTime assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), new immutable SimpleTimeZone( hours(0) ))).toSDLString() == "2004/10/31 14:30:15-GMT+00:00"); @@ -516,7 +492,7 @@ assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), new immutable SimpleTimeZone( hours(2)+minutes(10) ))).toSDLString() == "2004/10/31 14:30:15-GMT+02:10"); assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), new immutable SimpleTimeZone(-hours(5)-minutes(30) ))).toSDLString() == "2004/10/31 14:30:15-GMT-05:30"); assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), new immutable SimpleTimeZone( hours(2)+minutes( 3) ))).toSDLString() == "2004/10/31 14:30:15-GMT+02:03"); - assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), FracSec.from!"msecs"(123), new immutable SimpleTimeZone( hours(0) ))).toSDLString() == "2004/10/31 14:30:15.123-GMT+00:00"); + assert(Value(SysTime(DateTime(2004,10,31, 14,30,15), 123.msecs, new immutable SimpleTimeZone( hours(0) ))).toSDLString() == "2004/10/31 14:30:15.123-GMT+00:00"); // Duration assert( "12:14:42" == Value( days( 0)+hours(12)+minutes(14)+seconds(42)+msecs( 0)).toSDLString()); diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index 52cf55f..341df84 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -30,14 +30,14 @@ } -private Path[] temporary_files; +private NativePath[] temporary_files; -Path getTempDir() +NativePath getTempDir() { - return Path(std.file.tempDir()); + return NativePath(std.file.tempDir()); } -Path getTempFile(string prefix, string extension = null) +NativePath getTempFile(string prefix, string extension = null) { import std.uuid : randomUUID; @@ -99,13 +99,13 @@ } } -bool isEmptyDir(Path p) { +bool isEmptyDir(NativePath p) { foreach(DirEntry e; dirEntries(p.toNativeString(), SpanMode.shallow)) return false; return true; } -bool isWritableDir(Path p, bool create_if_missing = false) +bool isWritableDir(NativePath p, bool create_if_missing = false) { import std.random; auto fname = p ~ format("__dub_write_test_%08X", uniform(0, uint.max)); @@ -116,7 +116,7 @@ return true; } -Json jsonFromFile(Path file, bool silent_fail = false) { +Json jsonFromFile(NativePath file, bool silent_fail = false) { if( silent_fail && !existsFile(file) ) return Json.emptyObject; auto f = openFile(file.toNativeString(), FileMode.read); scope(exit) f.close(); @@ -124,7 +124,7 @@ return parseJsonString(text, file.toNativeString()); } -Json jsonFromZip(Path zip, string filename) { +Json jsonFromZip(NativePath zip, string filename) { import std.zip : ZipArchive; auto f = openFile(zip, FileMode.read); ubyte[] b = new ubyte[cast(size_t)f.size]; @@ -135,7 +135,7 @@ return parseJsonString(text, zip.toNativeString~"/"~filename); } -void writeJsonFile(Path path, Json json) +void writeJsonFile(NativePath path, Json json) { auto f = openFile(path, FileMode.createTrunc); scope(exit) f.close(); @@ -143,10 +143,10 @@ } /// Performs a write->delete->rename sequence to atomically "overwrite" the destination file -void atomicWriteJsonFile(Path path, Json json) +void atomicWriteJsonFile(NativePath path, Json json) { import std.random : uniform; - auto tmppath = path[0 .. $-1] ~ format("%s.%s.tmp", path.head, uniform(0, int.max)); + auto tmppath = path.parentPath ~ format("%s.%s.tmp", path.head, uniform(0, int.max)); auto f = openFile(tmppath, FileMode.createTrunc); scope (failure) { f.close(); @@ -163,7 +163,7 @@ return p[$-1] == '/'; } -bool existsDirectory(Path path) { +bool existsDirectory(NativePath path) { if( !existsFile(path) ) return false; auto fi = getFileInfo(path); return fi.isDirectory; @@ -265,7 +265,7 @@ } else assert(false); } /// ditto -void download(URL url, Path filename) +void download(URL url, NativePath filename) { download(url.toString(), filename.toNativeString()); } @@ -442,25 +442,27 @@ return ret.data; } -string determineModuleName(BuildSettings settings, Path file, Path base_path) +string determineModuleName(BuildSettings settings, NativePath file, NativePath base_path) { import std.algorithm : map; + import std.array : array; + import std.range : walkLength; assert(base_path.absolute); if (!file.absolute) file = base_path ~ file; size_t path_skip = 0; - foreach (ipath; settings.importPaths.map!(p => Path(p))) { + foreach (ipath; settings.importPaths.map!(p => NativePath(p))) { if (!ipath.absolute) ipath = base_path ~ ipath; assert(!ipath.empty); - if (file.startsWith(ipath) && ipath.length > path_skip) - path_skip = ipath.length; + if (file.startsWith(ipath) && ipath.bySegment.walkLength > path_skip) + path_skip = ipath.bySegment.walkLength; } enforce(path_skip > 0, format("Source file '%s' not found in any import path.", file.toNativeString())); - auto mpath = file[path_skip .. file.length]; + auto mpath = file.bySegment.array[path_skip .. $]; auto ret = appender!string; //search for module keyword in file diff --git a/source/dub/internal/vibecompat/core/file.d b/source/dub/internal/vibecompat/core/file.d index 0dcbe14..26321d4 100644 --- a/source/dub/internal/vibecompat/core/file.d +++ b/source/dub/internal/vibecompat/core/file.d @@ -54,7 +54,7 @@ /** Opens a file stream with the specified mode. */ -RangeFile openFile(Path path, FileMode mode = FileMode.read) +RangeFile openFile(NativePath path, FileMode mode = FileMode.read) { string fmode; final switch(mode){ @@ -70,14 +70,14 @@ /// ditto RangeFile openFile(string path, FileMode mode = FileMode.read) { - return openFile(Path(path), mode); + return openFile(NativePath(path), mode); } /** Moves or renames a file. */ -void moveFile(Path from, Path to) +void moveFile(NativePath from, NativePath to) { moveFile(from.toNativeString(), to.toNativeString()); } @@ -93,8 +93,8 @@ Note that attributes and time stamps are currently not retained. Params: - from = Path of the source file - to = Path for the destination file + from = NativePath of the source file + to = NativePath for the destination file overwrite = If true, any file existing at the destination path will be overwritten. If this is false, an excpetion will be thrown should a file already exist at the destination path. @@ -102,7 +102,7 @@ Throws: An Exception if the copy operation fails for some reason. */ -void copyFile(Path from, Path to, bool overwrite = false) +void copyFile(NativePath from, NativePath to, bool overwrite = false) { enforce(existsFile(from), "Source file does not exist."); @@ -138,13 +138,13 @@ /// ditto void copyFile(string from, string to) { - copyFile(Path(from), Path(to)); + copyFile(NativePath(from), NativePath(to)); } version (Windows) extern(Windows) int CreateHardLinkW(in wchar* to, in wchar* from, void* attr=null); // guess whether 2 files are identical, ignores filename and content -private bool sameFile(Path a, Path b) +private bool sameFile(NativePath a, NativePath b) { version (Posix) { auto st_a = std.file.DirEntry(a.toNativeString).statBuf; @@ -159,7 +159,7 @@ /** Creates a hardlink. */ -void hardLinkFile(Path from, Path to, bool overwrite = false) +void hardLinkFile(NativePath from, NativePath to, bool overwrite = false) { if (existsFile(to)) { enforce(overwrite, "Destination file already exists."); @@ -189,7 +189,7 @@ /** Removes a file */ -void removeFile(Path path) +void removeFile(NativePath path) { removeFile(path.toNativeString()); } @@ -201,7 +201,7 @@ /** Checks if a file exists */ -bool existsFile(Path path) { +bool existsFile(NativePath path) { return existsFile(path.toNativeString()); } /// ditto @@ -214,36 +214,34 @@ Returns false if the file does not exist. */ -FileInfo getFileInfo(Path path) +FileInfo getFileInfo(NativePath path) { - static if (__VERSION__ >= 2064) - auto ent = std.file.DirEntry(path.toNativeString()); - else auto ent = std.file.dirEntry(path.toNativeString()); + auto ent = std.file.DirEntry(path.toNativeString()); return makeFileInfo(ent); } /// ditto FileInfo getFileInfo(string path) { - return getFileInfo(Path(path)); + return getFileInfo(NativePath(path)); } /** Creates a new directory. */ -void createDirectory(Path path) +void createDirectory(NativePath path) { mkdir(path.toNativeString()); } /// ditto void createDirectory(string path) { - createDirectory(Path(path)); + createDirectory(NativePath(path)); } /** Enumerates all files in the specified directory. */ -void listDirectory(Path path, scope bool delegate(FileInfo info) del) +void listDirectory(NativePath path, scope bool delegate(FileInfo info) del) { foreach( DirEntry ent; dirEntries(path.toNativeString(), SpanMode.shallow) ) if( !del(makeFileInfo(ent)) ) @@ -252,10 +250,10 @@ /// ditto void listDirectory(string path, scope bool delegate(FileInfo info) del) { - listDirectory(Path(path), del); + listDirectory(NativePath(path), del); } /// ditto -int delegate(scope int delegate(ref FileInfo)) iterateDirectory(Path path) +int delegate(scope int delegate(ref FileInfo)) iterateDirectory(NativePath path) { int iterator(scope int delegate(ref FileInfo) del){ int ret = 0; @@ -270,16 +268,16 @@ /// ditto int delegate(scope int delegate(ref FileInfo)) iterateDirectory(string path) { - return iterateDirectory(Path(path)); + return iterateDirectory(NativePath(path)); } /** Returns the current working directory. */ -Path getWorkingDirectory() +NativePath getWorkingDirectory() { - return Path(std.file.getcwd()); + return NativePath(std.file.getcwd()); } diff --git a/source/dub/internal/vibecompat/data/json.d b/source/dub/internal/vibecompat/data/json.d index a2705a3..c352bf3 100644 --- a/source/dub/internal/vibecompat/data/json.d +++ b/source/dub/internal/vibecompat/data/json.d @@ -2102,15 +2102,13 @@ private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception") { - static if (__VERSION__ >= 2065) enforceEx!JSONException(cond, message, file, line); - else if (!cond) throw new JSONException(message); + enforceEx!JSONException(cond, message, file, line); } private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int err_line) { auto errmsg() { return format("%s(%s): Error: %s", err_file, err_line+1, message); } - static if (__VERSION__ >= 2065) enforceEx!JSONException(cond, errmsg, file, line); - else if (!cond) throw new JSONException(errmsg); + enforceEx!JSONException(cond, errmsg, file, line); } private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int* err_line) diff --git a/source/dub/internal/vibecompat/data/serialization.d b/source/dub/internal/vibecompat/data/serialization.d index ccbb115..0f9b7c4 100644 --- a/source/dub/internal/vibecompat/data/serialization.d +++ b/source/dub/internal/vibecompat/data/serialization.d @@ -196,7 +196,7 @@ } /// -static if (__VERSION__ >= 2065) unittest { +unittest { import dub.internal.vibecompat.data.json; static struct SizeI { @@ -266,7 +266,7 @@ } /// -static if (__VERSION__ >= 2065) unittest { +unittest { import dub.internal.vibecompat.data.json; static struct SizeI { @@ -292,7 +292,7 @@ private void serializeImpl(Serializer, alias Policy, T, ATTRIBUTES...)(ref Serializer serializer, T value) { import std.typecons : Nullable, Tuple, tuple; - static if (__VERSION__ >= 2067) import std.typecons : BitFlags; + import std.typecons : BitFlags; static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); @@ -356,7 +356,7 @@ } else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) { if (value.isNull()) serializeImpl!(Serializer, Policy, typeof(null))(serializer, null); else serializeImpl!(Serializer, Policy, typeof(value.get()), ATTRIBUTES)(serializer, value.get()); - } else static if (__VERSION__ >= 2067 && is(T == BitFlags!E, E)) { + } else static if (is(T == BitFlags!E, E)) { size_t cnt = 0; foreach (v; EnumMembers!E) if (value & v) @@ -450,7 +450,7 @@ private T deserializeImpl(T, alias Policy, Serializer, ATTRIBUTES...)(ref Serializer deserializer) { import std.typecons : Nullable; - static if (__VERSION__ >= 2067) import std.typecons : BitFlags; + import std.typecons : BitFlags; static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); @@ -496,7 +496,7 @@ } else static if (isInstanceOf!(Nullable, T)) { if (deserializer.tryReadNull()) return T.init; return T(deserializeImpl!(typeof(T.init.get()), Policy, Serializer, ATTRIBUTES)(deserializer)); - } else static if (__VERSION__ >= 2067 && is(T == BitFlags!E, E)) { + } else static if (is(T == BitFlags!E, E)) { T ret; deserializer.readArray!(E[])((sz) {}, { ret |= deserializeImpl!(E, Policy, Serializer, ATTRIBUTES)(deserializer); @@ -1125,7 +1125,8 @@ test(12.0f, "V(f)(12)"); assert(serialize!TestSerializer(null) == "null"); test(["hello", "world"], "A(AAya)[2][AE(Aya,0)(V(Aya)(hello))AE(Aya,0)AE(Aya,1)(V(Aya)(world))AE(Aya,1)]A(AAya)"); - test(["hello": "world"], "D(HAyaAya){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(HAyaAya)"); + string mangleOfAA = (string[string]).mangleof; + test(["hello": "world"], "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")"); test(cast(int*)null, "null"); int i = 42; test(&i, "V(i)(42)"); @@ -1275,7 +1276,6 @@ assert(s.serializeToJson().deserializeJson!S() == s); } -static if (__VERSION__ >= 2067) unittest { // test BitFlags serialization import std.typecons : BitFlags; diff --git a/source/dub/internal/vibecompat/inet/path.d b/source/dub/internal/vibecompat/inet/path.d index f5bd81f..047ac5d 100644 --- a/source/dub/internal/vibecompat/inet/path.d +++ b/source/dub/internal/vibecompat/inet/path.d @@ -7,7 +7,8 @@ */ module dub.internal.vibecompat.inet.path; -version (Have_vibe_d_core) public import vibe.inet.path; +version (Have_vibe_core) public import vibe.core.path; +else version (Have_vibe_d_core) public import vibe.inet.path; else: import std.algorithm; @@ -17,6 +18,9 @@ import std.string; +deprecated("Use NativePath instead.") +alias Path = NativePath; + /** Represents an absolute or relative file system path. @@ -24,18 +28,21 @@ are done to disallow invalid operations such as concatenating two absolute paths. It also validates path strings and allows for easy checking of malicious relative paths. */ -struct Path { +struct NativePath { private { immutable(PathEntry)[] m_nodes; bool m_absolute = false; bool m_endsWithSlash = false; } - /// Constructs a Path object by parsing a path string. + alias Segment = PathEntry; + + alias bySegment = nodes; + + /// Constructs a NativePath object by parsing a path string. this(string pathstr) { - static if (__VERSION__ < 2066) m_nodes = splitPath(pathstr).idup; - else m_nodes = splitPath(pathstr); + m_nodes = splitPath(pathstr); m_absolute = (pathstr.startsWith("/") || m_nodes.length > 0 && (m_nodes[0].toString().countUntil(':')>0 || m_nodes[0] == "\\")); m_endsWithSlash = pathstr.endsWith("/"); } @@ -109,7 +116,7 @@ return ret.data; } - /// Converts the Path object to a native path string (backslash as path separator on Windows). + /// Converts the NativePath object to a native path string (backslash as path separator on Windows). string toNativeString() const { if (m_nodes.empty) { @@ -140,7 +147,7 @@ } /// Tests if `rhs` is an anchestor or the same as this path. - bool startsWith(const Path rhs) const { + bool startsWith(const NativePath rhs) const { if( rhs.m_nodes.length > m_nodes.length ) return false; foreach( i; 0 .. rhs.m_nodes.length ) if( m_nodes[i] != rhs.m_nodes[i] ) @@ -149,7 +156,7 @@ } /// Computes the relative path from `parentPath` to this path. - Path relativeTo(const Path parentPath) const { + NativePath relativeTo(const NativePath parentPath) const { assert(this.absolute && parentPath.absolute, "Determining relative path between non-absolute paths."); version(Windows){ // a path such as ..\C:\windows is not valid, so force the path to stay absolute in this case @@ -165,11 +172,11 @@ nup++; } assert(m_nodes.length >= parentPath.length - nup); - Path ret = Path(null, false); + NativePath ret = NativePath(null, false); assert(m_nodes.length >= parentPath.length - nup); ret.m_endsWithSlash = true; foreach( i; 0 .. nup ) ret ~= ".."; - ret ~= Path(m_nodes[parentPath.length-nup .. $], false); + ret ~= NativePath(m_nodes[parentPath.length-nup .. $], false); ret.m_endsWithSlash = this.m_endsWithSlash; return ret; } @@ -178,7 +185,7 @@ @property ref immutable(PathEntry) head() const { enforce(m_nodes.length > 0, "Getting head of empty path."); return m_nodes[$-1]; } /// The parent path - @property Path parentPath() const { return this[0 .. length-1]; } + @property NativePath parentPath() const { return this[0 .. length-1]; } /// The ist of path entries of which this path is composed @property immutable(PathEntry)[] nodes() const { return m_nodes; } @@ -198,16 +205,16 @@ @property bool external() const { return !m_absolute && m_nodes.length > 0 && m_nodes[0].m_name == ".."; } ref immutable(PathEntry) opIndex(size_t idx) const { return m_nodes[idx]; } - Path opSlice(size_t start, size_t end) const { - auto ret = Path(m_nodes[start .. end], start == 0 ? absolute : false); + NativePath opSlice(size_t start, size_t end) const { + auto ret = NativePath(m_nodes[start .. end], start == 0 ? absolute : false); if( end == m_nodes.length ) ret.m_endsWithSlash = m_endsWithSlash; return ret; } size_t opDollar(int dim)() const if(dim == 0) { return m_nodes.length; } - Path opBinary(string OP)(const Path rhs) const if( OP == "~" ) { - Path ret; + NativePath opBinary(string OP)(const NativePath rhs) const if( OP == "~" ) { + NativePath ret; ret.m_nodes = m_nodes; ret.m_absolute = m_absolute; ret.m_endsWithSlash = rhs.m_endsWithSlash; @@ -230,14 +237,14 @@ return ret; } - Path opBinary(string OP)(string rhs) const if( OP == "~" ) { assert(rhs.length > 0, "Cannot append empty path string."); return opBinary!"~"(Path(rhs)); } - Path opBinary(string OP)(PathEntry rhs) const if( OP == "~" ) { assert(rhs.toString().length > 0, "Cannot append empty path string."); return opBinary!"~"(Path(rhs)); } - void opOpAssign(string OP)(string rhs) if( OP == "~" ) { assert(rhs.length > 0, "Cannot append empty path string."); opOpAssign!"~"(Path(rhs)); } - void opOpAssign(string OP)(PathEntry rhs) if( OP == "~" ) { assert(rhs.toString().length > 0, "Cannot append empty path string."); opOpAssign!"~"(Path(rhs)); } - void opOpAssign(string OP)(Path rhs) if( OP == "~" ) { auto p = this ~ rhs; m_nodes = p.m_nodes; m_endsWithSlash = rhs.m_endsWithSlash; } + NativePath opBinary(string OP)(string rhs) const if( OP == "~" ) { assert(rhs.length > 0, "Cannot append empty path string."); return opBinary!"~"(NativePath(rhs)); } + NativePath opBinary(string OP)(PathEntry rhs) const if( OP == "~" ) { assert(rhs.toString().length > 0, "Cannot append empty path string."); return opBinary!"~"(NativePath(rhs)); } + void opOpAssign(string OP)(string rhs) if( OP == "~" ) { assert(rhs.length > 0, "Cannot append empty path string."); opOpAssign!"~"(NativePath(rhs)); } + void opOpAssign(string OP)(PathEntry rhs) if( OP == "~" ) { assert(rhs.toString().length > 0, "Cannot append empty path string."); opOpAssign!"~"(NativePath(rhs)); } + void opOpAssign(string OP)(NativePath rhs) if( OP == "~" ) { auto p = this ~ rhs; m_nodes = p.m_nodes; m_endsWithSlash = rhs.m_endsWithSlash; } /// Tests two paths for equality using '=='. - bool opEquals(ref const Path rhs) const { + bool opEquals(ref const NativePath rhs) const { if( m_absolute != rhs.m_absolute ) return false; if( m_endsWithSlash != rhs.m_endsWithSlash ) return false; if( m_nodes.length != rhs.length ) return false; @@ -247,9 +254,9 @@ return true; } /// ditto - bool opEquals(const Path other) const { return opEquals(other); } + bool opEquals(const NativePath other) const { return opEquals(other); } - int opCmp(ref const Path rhs) const { + int opCmp(ref const NativePath rhs) const { if( m_absolute != rhs.m_absolute ) return cast(int)m_absolute - cast(int)rhs.m_absolute; foreach( i; 0 .. min(m_nodes.length, rhs.m_nodes.length) ) if( m_nodes[i] != rhs.m_nodes[i] ) @@ -284,7 +291,9 @@ string toString() const pure { return m_name; } - Path opBinary(string OP)(PathEntry rhs) const if( OP == "~" ) { return Path([this, rhs], false); } + @property string name() const { return m_name; } + + NativePath opBinary(string OP)(PathEntry rhs) const if( OP == "~" ) { return NativePath([this, rhs], false); } bool opEquals(ref const PathEntry rhs) const { return m_name == rhs.m_name; } bool opEquals(PathEntry rhs) const { return m_name == rhs.m_name; } @@ -303,8 +312,8 @@ /// Joins two path strings. subpath must be relative. string joinPath(string basepath, string subpath) { - Path p1 = Path(basepath); - Path p2 = Path(subpath); + NativePath p1 = NativePath(basepath); + NativePath p2 = NativePath(subpath); return (p1 ~ p2).toString(); } @@ -347,13 +356,13 @@ unittest { - Path p; + NativePath p; assert(p.toNativeString() == "."); p.endsWithSlash = true; version(Windows) assert(p.toNativeString() == ".\\"); else assert(p.toNativeString() == "./"); - p = Path("test/"); + p = NativePath("test/"); version(Windows) assert(p.toNativeString() == "test\\"); else assert(p.toNativeString() == "test/"); p.endsWithSlash = false; @@ -364,7 +373,7 @@ { { auto unc = "\\\\server\\share\\path"; - auto uncp = Path(unc); + auto uncp = NativePath(unc); uncp.normalize(); version(Windows) assert(uncp.toNativeString() == unc); assert(uncp.absolute); @@ -373,7 +382,7 @@ { auto abspath = "/test/path/"; - auto abspathp = Path(abspath); + auto abspathp = NativePath(abspath); assert(abspathp.toString() == abspath); version(Windows) {} else assert(abspathp.toNativeString() == abspath); assert(abspathp.absolute); @@ -385,7 +394,7 @@ { auto relpath = "test/path/"; - auto relpathp = Path(relpath); + auto relpathp = NativePath(relpath); assert(relpathp.toString() == relpath); version(Windows) assert(relpathp.toNativeString() == "test\\path\\"); else assert(relpathp.toNativeString() == relpath); @@ -398,7 +407,7 @@ { auto winpath = "C:\\windows\\test"; - auto winpathp = Path(winpath); + auto winpathp = NativePath(winpath); version(Windows) { assert(winpathp.toString() == "C:/windows/test", winpathp.toString()); assert(winpathp.toNativeString() == winpath); @@ -416,7 +425,7 @@ { auto dotpath = "/test/../test2/././x/y"; - auto dotpathp = Path(dotpath); + auto dotpathp = NativePath(dotpath); assert(dotpathp.toString() == "/test/../test2/././x/y"); dotpathp.normalize(); assert(dotpathp.toString() == "/test2/x/y"); @@ -424,7 +433,7 @@ { auto dotpath = "/test/..////test2//./x/y"; - auto dotpathp = Path(dotpath); + auto dotpathp = NativePath(dotpath); assert(dotpathp.toString() == "/test/..////test2//./x/y"); dotpathp.normalize(); assert(dotpathp.toString() == "/test2/x/y"); @@ -432,46 +441,46 @@ { auto parentpath = "/path/to/parent"; - auto parentpathp = Path(parentpath); + auto parentpathp = NativePath(parentpath); auto subpath = "/path/to/parent/sub/"; - auto subpathp = Path(subpath); + auto subpathp = NativePath(subpath); auto subpath_rel = "sub/"; assert(subpathp.relativeTo(parentpathp).toString() == subpath_rel); auto subfile = "/path/to/parent/child"; - auto subfilep = Path(subfile); + auto subfilep = NativePath(subfile); auto subfile_rel = "child"; assert(subfilep.relativeTo(parentpathp).toString() == subfile_rel); } { // relative paths across Windows devices are not allowed version (Windows) { - auto p1 = Path("\\\\server\\share"); assert(p1.absolute); - auto p2 = Path("\\\\server\\othershare"); assert(p2.absolute); - auto p3 = Path("\\\\otherserver\\share"); assert(p3.absolute); - auto p4 = Path("C:\\somepath"); assert(p4.absolute); - auto p5 = Path("C:\\someotherpath"); assert(p5.absolute); - auto p6 = Path("D:\\somepath"); assert(p6.absolute); - assert(p4.relativeTo(p5) == Path("../somepath")); - assert(p4.relativeTo(p6) == Path("C:\\somepath")); - assert(p4.relativeTo(p1) == Path("C:\\somepath")); - assert(p1.relativeTo(p2) == Path("../share")); - assert(p1.relativeTo(p3) == Path("\\\\server\\share")); - assert(p1.relativeTo(p4) == Path("\\\\server\\share")); + auto p1 = NativePath("\\\\server\\share"); assert(p1.absolute); + auto p2 = NativePath("\\\\server\\othershare"); assert(p2.absolute); + auto p3 = NativePath("\\\\otherserver\\share"); assert(p3.absolute); + auto p4 = NativePath("C:\\somepath"); assert(p4.absolute); + auto p5 = NativePath("C:\\someotherpath"); assert(p5.absolute); + auto p6 = NativePath("D:\\somepath"); assert(p6.absolute); + assert(p4.relativeTo(p5) == NativePath("../somepath")); + assert(p4.relativeTo(p6) == NativePath("C:\\somepath")); + assert(p4.relativeTo(p1) == NativePath("C:\\somepath")); + assert(p1.relativeTo(p2) == NativePath("../share")); + assert(p1.relativeTo(p3) == NativePath("\\\\server\\share")); + assert(p1.relativeTo(p4) == NativePath("\\\\server\\share")); } } } unittest { - assert(Path("/foo/bar/baz").relativeTo(Path("/foo")).toString == "bar/baz"); - assert(Path("/foo/bar/baz/").relativeTo(Path("/foo")).toString == "bar/baz/"); - assert(Path("/foo/bar").relativeTo(Path("/foo")).toString == "bar"); - assert(Path("/foo/bar/").relativeTo(Path("/foo")).toString == "bar/"); - assert(Path("/foo").relativeTo(Path("/foo/bar")).toString() == ".."); - assert(Path("/foo/").relativeTo(Path("/foo/bar")).toString() == "../"); - assert(Path("/foo/baz").relativeTo(Path("/foo/bar/baz")).toString() == "../../baz"); - assert(Path("/foo/baz/").relativeTo(Path("/foo/bar/baz")).toString() == "../../baz/"); - assert(Path("/foo/").relativeTo(Path("/foo/bar/baz")).toString() == "../../"); - assert(Path("/foo/").relativeTo(Path("/foo/bar/baz/mumpitz")).toString() == "../../../"); - assert(Path("/foo").relativeTo(Path("/foo")).toString() == ""); - assert(Path("/foo/").relativeTo(Path("/foo")).toString() == ""); + assert(NativePath("/foo/bar/baz").relativeTo(NativePath("/foo")).toString == "bar/baz"); + assert(NativePath("/foo/bar/baz/").relativeTo(NativePath("/foo")).toString == "bar/baz/"); + assert(NativePath("/foo/bar").relativeTo(NativePath("/foo")).toString == "bar"); + assert(NativePath("/foo/bar/").relativeTo(NativePath("/foo")).toString == "bar/"); + assert(NativePath("/foo").relativeTo(NativePath("/foo/bar")).toString() == ".."); + assert(NativePath("/foo/").relativeTo(NativePath("/foo/bar")).toString() == "../"); + assert(NativePath("/foo/baz").relativeTo(NativePath("/foo/bar/baz")).toString() == "../../baz"); + assert(NativePath("/foo/baz/").relativeTo(NativePath("/foo/bar/baz")).toString() == "../../baz/"); + assert(NativePath("/foo/").relativeTo(NativePath("/foo/bar/baz")).toString() == "../../"); + assert(NativePath("/foo/").relativeTo(NativePath("/foo/bar/baz/mumpitz")).toString() == "../../../"); + assert(NativePath("/foo").relativeTo(NativePath("/foo")).toString() == ""); + assert(NativePath("/foo/").relativeTo(NativePath("/foo")).toString() == ""); } diff --git a/source/dub/internal/vibecompat/inet/url.d b/source/dub/internal/vibecompat/inet/url.d index 9115c9c..6016af5 100644 --- a/source/dub/internal/vibecompat/inet/url.d +++ b/source/dub/internal/vibecompat/inet/url.d @@ -27,7 +27,7 @@ private { string m_schema; string m_pathString; - Path m_path; + NativePath m_path; string m_host; ushort m_port; string m_username; @@ -37,7 +37,7 @@ } /// Constructs a new URL object from its components. - this(string schema, string host, ushort port, Path path) + this(string schema, string host, ushort port, NativePath path) { m_schema = schema; m_host = host; @@ -46,7 +46,7 @@ m_pathString = path.toString(); } /// ditto - this(string schema, Path path) + this(string schema, NativePath path) { this(schema, null, 0, path); } @@ -124,9 +124,9 @@ @property string pathString() const { return m_pathString; } /// The path part of the URL - @property Path path() const { return m_path; } + @property NativePath path() const { return m_path; } /// ditto - @property void path(Path p) + @property void path(NativePath p) { m_path = p; auto pstr = p.toString(); @@ -193,7 +193,7 @@ } m_pathString = str; - m_path = Path(decode(str)); + m_path = NativePath(decode(str)); } /// The URL to the parent path with query string and anchor stripped. @@ -239,9 +239,9 @@ return path.startsWith(rhs.m_path); } - URL opBinary(string OP)(Path rhs) const if( OP == "~" ) { return URL(m_schema, m_host, m_port, m_path ~ rhs); } + URL opBinary(string OP)(NativePath rhs) const if( OP == "~" ) { return URL(m_schema, m_host, m_port, m_path ~ rhs); } URL opBinary(string OP)(PathEntry rhs) const if( OP == "~" ) { return URL(m_schema, m_host, m_port, m_path ~ rhs); } - void opOpAssign(string OP)(Path rhs) if( OP == "~" ) { m_path ~= rhs; } + void opOpAssign(string OP)(NativePath rhs) if( OP == "~" ) { m_path ~= rhs; } void opOpAssign(string OP)(PathEntry rhs) if( OP == "~" ) { m_path ~= rhs; } /// Tests two URLs for equality using '=='. @@ -266,7 +266,7 @@ auto url = URL.parse("https://www.example.net/index.html"); assert(url.schema == "https", url.schema); assert(url.host == "www.example.net", url.host); - assert(url.path == Path("/index.html"), url.path.toString()); + assert(url.path == NativePath("/index.html"), url.path.toString()); url = URL.parse("http://jo.doe:password@sub.www.example.net:4711/sub2/index.html?query#anchor"); assert(url.schema == "http", url.schema); @@ -278,9 +278,9 @@ assert(url.queryString == "query", url.queryString); assert(url.anchor == "anchor", url.anchor); - url = URL("http://localhost")~Path("packages"); + url = URL("http://localhost")~NativePath("packages"); assert(url.toString() == "http://localhost/packages", url.toString()); - url = URL("http://localhost/")~Path("packages"); + url = URL("http://localhost/")~NativePath("packages"); assert(url.toString() == "http://localhost/packages", url.toString()); } diff --git a/source/dub/package_.d b/source/dub/package_.d index 0f5cf2d..8a2e6f6 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -60,8 +60,8 @@ */ class Package { private { - Path m_path; - Path m_infoFile; + NativePath m_path; + NativePath m_infoFile; PackageRecipe m_info; PackageRecipe m_rawRecipe; Package m_parentPackage; @@ -79,7 +79,7 @@ instead of the one declared in the package recipe, or the one determined by invoking the VCS (GIT currently). */ - this(Json json_recipe, Path root = Path(), Package parent = null, string version_override = "") + this(Json json_recipe, NativePath root = NativePath(), Package parent = null, string version_override = "") { import dub.recipe.json; @@ -88,7 +88,7 @@ this(recipe, root, parent, version_override); } /// ditto - this(PackageRecipe recipe, Path root = Path(), Package parent = null, string version_override = "") + this(PackageRecipe recipe, NativePath root = NativePath(), Package parent = null, string version_override = "") { // save the original recipe m_rawRecipe = recipe.clone; @@ -117,7 +117,6 @@ m_info = recipe; fillWithDefaults(); - simpleLint(); } /** Searches the given directory for package recipe files. @@ -129,13 +128,13 @@ Returns the full path to the package file, if any was found. Otherwise returns an empty path. */ - static Path findPackageFile(Path directory) + static NativePath findPackageFile(NativePath directory) { foreach (file; packageInfoFiles) { auto filename = directory ~ file.filename; if (existsFile(filename)) return filename; } - return Path.init; + return NativePath.init; } /** Constructs a `Package` using a package that is physically present on the local file system. @@ -150,7 +149,7 @@ instead of the one declared in the package recipe, or the one determined by invoking the VCS (GIT currently). */ - static Package load(Path root, Path recipe_file = Path.init, Package parent = null, string version_override = "") + static Package load(NativePath root, NativePath recipe_file = NativePath.init, Package parent = null, string version_override = "") { import dub.recipe.io; @@ -184,7 +183,7 @@ Note that this can be empty for packages that are not stored in the local file system. */ - @property Path path() const { return m_path; } + @property NativePath path() const { return m_path; } /** Accesses the version associated with this package. @@ -219,7 +218,7 @@ Note that this can be empty for packages that are not stored in the local file system. */ - @property Path recipePath() const { return m_infoFile; } + @property NativePath recipePath() const { return m_infoFile; } /** Returns the base package of this package. @@ -266,7 +265,7 @@ m_infoFile = m_path ~ defaultPackageFilename; } /// ditto - void storeInfo(Path path) + void storeInfo(NativePath path) const { enforce(!version_.isUnknown, "Trying to store a package with an 'unknown' version, this is not supported."); auto filename = path ~ defaultPackageFilename; @@ -527,14 +526,24 @@ PackageDependency[] getAllDependencies() const { auto ret = appender!(PackageDependency[]); - foreach (n, d; this.recipe.buildSettings.dependencies) - ret ~= PackageDependency(n, d); - foreach (ref c; this.recipe.configurations) - foreach (n, d; c.buildSettings.dependencies) - ret ~= PackageDependency(n, d); + getAllDependenciesRange().copy(ret); return ret.data; } + // Left as package until the final API for this has been found + package auto getAllDependenciesRange() + const { + return this.recipe.buildSettings.dependencies.byKeyValue + .map!(bs => PackageDependency(bs.key, bs.value)) + .chain( + this.recipe.configurations + .map!(c => c.buildSettings.dependencies.byKeyValue + .map!(bs => PackageDependency(bs.key, bs.value)) + ) + .joiner() + ); + } + /** Returns a description of the package for use in IDEs or build tools. */ @@ -641,7 +650,7 @@ 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(); + app_main_file = (NativePath(sf) ~ fil).toNativeString(); break; } } @@ -674,7 +683,7 @@ } } - private void simpleLint() + package void simpleLint() const { if (m_parentPackage) { if (m_parentPackage.path != path) { @@ -693,7 +702,7 @@ } } -private string determineVersionFromSCM(Path path) +private string determineVersionFromSCM(NativePath path) { // On Windows, which is slow at running external processes, // cache the version numbers that are determined using @@ -741,7 +750,7 @@ // determines the version of a package that is stored in a GIT working copy // by invoking the "git" executable -private string determineVersionWithGIT(Path path) +private string determineVersionWithGIT(NativePath path) { import std.process; import dub.semver; diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index a0f9f61..c210b2b 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -31,13 +31,13 @@ class PackageManager { private { Repository[LocalPackageType] m_repositories; - Path[] m_searchPath; + NativePath[] m_searchPath; Package[] m_packages; Package[] m_temporaryPackages; bool m_disableDefaultSearchPaths = false; } - this(Path user_path, Path system_path, bool refresh_packages = true) + this(NativePath user_path, NativePath system_path, bool refresh_packages = true) { m_repositories[LocalPackageType.user] = Repository(user_path); m_repositories[LocalPackageType.system] = Repository(system_path); @@ -46,14 +46,14 @@ /** Gets/sets the list of paths to search for local packages. */ - @property void searchPath(Path[] paths) + @property void searchPath(NativePath[] paths) { if (paths == m_searchPath) return; m_searchPath = paths.dup; refresh(false); } /// ditto - @property const(Path)[] searchPath() const { return m_searchPath; } + @property const(NativePath)[] searchPath() const { return m_searchPath; } /** Disables searching DUB's predefined search paths. */ @@ -66,15 +66,15 @@ /** Returns the effective list of search paths, including default ones. */ - @property const(Path)[] completeSearchPath() + @property const(NativePath)[] completeSearchPath() const { - auto ret = appender!(Path[])(); - ret.put(cast(Path[])m_searchPath); // work around Phobos 17251 + auto ret = appender!(NativePath[])(); + ret.put(cast(NativePath[])m_searchPath); // work around Phobos 17251 if (!m_disableDefaultSearchPaths) { - ret.put(cast(Path[])m_repositories[LocalPackageType.user].searchPath); - ret.put(cast(Path)m_repositories[LocalPackageType.user].packagePath); - ret.put(cast(Path[])m_repositories[LocalPackageType.system].searchPath); - ret.put(cast(Path)m_repositories[LocalPackageType.system].packagePath); + ret.put(cast(NativePath[])m_repositories[LocalPackageType.user].searchPath); + ret.put(cast(NativePath)m_repositories[LocalPackageType.user].packagePath); + ret.put(cast(NativePath[])m_repositories[LocalPackageType.system].searchPath); + ret.put(cast(NativePath)m_repositories[LocalPackageType.system].packagePath); } return ret.data; } @@ -127,7 +127,7 @@ } /// ditto - Package getPackage(string name, Version ver, Path path) + Package getPackage(string name, Version ver, NativePath path) { auto ret = getPackage(name, path); if (!ret || ret.version_ != ver) return null; @@ -135,13 +135,13 @@ } /// ditto - Package getPackage(string name, string ver, Path path) + Package getPackage(string name, string ver, NativePath path) { return getPackage(name, Version(ver), path); } /// ditto - Package getPackage(string name, Path path) + Package getPackage(string name, NativePath path) { foreach( p; getPackageIterator(name) ) if (p.path.startsWith(path)) @@ -165,14 +165,14 @@ the package gets loaded and cached for the next call to this function. Params: - path = Path to the root directory of the package + path = NativePath to the root directory of the package recipe_path = Optional path to the recipe file of the package allow_sub_packages = Also return a sub package if it resides in the given folder Returns: The packages loaded from the given path Throws: Throws an exception if no package can be loaded */ - Package getOrLoadPackage(Path path, Path recipe_path = Path.init, bool allow_sub_packages = false) + Package getOrLoadPackage(NativePath path, NativePath recipe_path = NativePath.init, bool allow_sub_packages = false) { path.endsWithSlash = true; foreach (p; getPackageIterator()) @@ -244,10 +244,10 @@ By default, managed folders are "~/.dub/packages" and "/var/lib/dub/packages". */ - bool isManagedPath(Path path) + bool isManagedPath(NativePath path) const { foreach (rep; m_repositories) { - Path rpath = rep.packagePath; + NativePath rpath = rep.packagePath; if (path.startsWith(rpath)) return true; } @@ -313,7 +313,7 @@ writeLocalPackageOverridesFile(scope_); } /// ditto - void addOverride(LocalPackageType scope_, string package_, Dependency version_spec, Path target) + void addOverride(LocalPackageType scope_, string package_, Dependency version_spec, NativePath target) { m_repositories[scope_].overrides ~= PackageOverride(package_, version_spec, target); writeLocalPackageOverridesFile(scope_); @@ -336,8 +336,10 @@ /// Extracts the package supplied as a path to it's zip file to the /// destination and sets a version field in the package description. - Package storeFetchedPackage(Path zip_file_path, Json package_info, Path destination) + Package storeFetchedPackage(NativePath zip_file_path, Json package_info, NativePath destination) { + import std.range : walkLength; + 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 .. $]; @@ -361,11 +363,12 @@ logDebug("Extracting from zip."); // In a github zip, the actual contents are in a subfolder - Path zip_prefix; + alias PSegment = typeof(NativePath.init.head); + PSegment[] zip_prefix; outer: foreach(ArchiveMember am; archive.directory) { - auto path = Path(am.name); + auto path = NativePath(am.name).bySegment.array; foreach (fil; packageInfoFiles) - if (path.length == 2 && path.head.toString == fil.filename) { + if (path.length == 2 && path[$-1].toString == fil.filename) { zip_prefix = path[0 .. $-1]; break outer; } @@ -373,10 +376,11 @@ logDebug("zip root folder: %s", zip_prefix); - Path getCleanedPath(string fileName) { - auto path = Path(fileName); - if(zip_prefix != Path() && !path.startsWith(zip_prefix)) return Path(); - return path[zip_prefix.length..path.length]; + NativePath getCleanedPath(string fileName) { + auto path = NativePath(fileName); + if (zip_prefix.length && !path.bySegment.startsWith(zip_prefix)) return NativePath.init; + static if (is(typeof(path[0 .. 1]))) return path[zip_prefix.length .. $]; + else return NativePath(path.bySegment.array[zip_prefix.length .. $]); } static void setAttributes(string path, ArchiveMember am) @@ -396,7 +400,7 @@ foreach(ArchiveMember a; archive.directory) { auto cleanedPath = getCleanedPath(a.name); if(cleanedPath.empty) continue; - auto dst_path = destination~cleanedPath; + auto dst_path = destination ~ cleanedPath; logDebug("Creating %s", cleanedPath); if( dst_path.endsWithSlash ){ @@ -417,7 +421,7 @@ logDebug("%s file(s) copied.", to!string(countFiles)); // overwrite dub.json (this one includes a version field) - auto pack = Package.load(destination, Path.init, null, package_info["version"].get!string); + auto pack = Package.load(destination, NativePath.init, null, package_info["version"].get!string); if (pack.recipePath.head != defaultPackageFilename) // Storeinfo saved a default file, this could be different to the file from the zip. @@ -464,7 +468,7 @@ remove(pack); } - Package addLocalPackage(Path path, string verName, LocalPackageType type) + Package addLocalPackage(NativePath path, string verName, LocalPackageType type) { path.endsWithSlash = true; auto pack = Package.load(path); @@ -490,7 +494,7 @@ return pack; } - void removeLocalPackage(Path path, LocalPackageType type) + void removeLocalPackage(NativePath path, LocalPackageType type) { path.endsWithSlash = true; @@ -514,14 +518,14 @@ } /// For the given type add another path where packages will be looked up. - void addSearchPath(Path path, LocalPackageType type) + void addSearchPath(NativePath path, LocalPackageType type) { m_repositories[type].searchPath ~= path; writeLocalPackageList(type); } /// Removes a search path from the given type. - void removeSearchPath(Path path, LocalPackageType type) + void removeSearchPath(NativePath path, LocalPackageType type) { m_repositories[type].searchPath = m_repositories[type].searchPath.filter!(p => p != path)().array(); writeLocalPackageList(type); @@ -534,9 +538,9 @@ // load locally defined packages void scanLocalPackages(LocalPackageType type) { - Path list_path = m_repositories[type].packagePath; + NativePath list_path = m_repositories[type].packagePath; Package[] packs; - Path[] paths; + NativePath[] paths; if (!m_disableDefaultSearchPaths) try { auto local_package_file = list_path ~ LocalPackagesFilename; logDiagnostic("Looking for local package map at %s", local_package_file.toNativeString()); @@ -547,7 +551,7 @@ foreach( pentry; packlist ){ try { auto name = pentry["name"].get!string; - auto path = Path(pentry["path"].get!string); + auto path = NativePath(pentry["path"].get!string); if (name == "*") { paths ~= path; } else { @@ -596,7 +600,7 @@ auto old_packages = m_packages; // rescan the system and user package folder - void scanPackageFolder(Path path) + void scanPackageFolder(NativePath path) { if( path.existsDirectory() ){ logDebug("iterating dir %s", path.toNativeString()); @@ -653,7 +657,7 @@ ovr.package_ = entry["name"].get!string; ovr.version_ = Dependency(entry["version"].get!string); if (auto pv = "targetVersion" in entry) ovr.targetVersion = Version(pv.get!string); - if (auto pv = "targetPath" in entry) ovr.targetPath = Path(pv.get!string); + if (auto pv = "targetPath" in entry) ovr.targetPath = NativePath(pv.get!string); m_repositories[type].overrides ~= ovr; } } @@ -673,18 +677,18 @@ string[] ignored_files = []; SHA1 sha1; foreach(file; dirEntries(pack.path.toNativeString(), SpanMode.depth)) { - if(file.isDir && ignored_directories.canFind(Path(file.name).head.toString())) + if(file.isDir && ignored_directories.canFind(NativePath(file.name).head.toString())) continue; - else if(ignored_files.canFind(Path(file.name).head.toString())) + else if(ignored_files.canFind(NativePath(file.name).head.toString())) continue; - sha1.put(cast(ubyte[])Path(file.name).head.toString()); + sha1.put(cast(ubyte[])NativePath(file.name).head.toString()); if(file.isDir) { - logDebug("Hashed directory name %s", Path(file.name).head); + logDebug("Hashed directory name %s", NativePath(file.name).head); } else { - sha1.put(openFile(Path(file.name)).readAll()); - logDebug("Hashed file contents from %s", Path(file.name).head); + sha1.put(openFile(NativePath(file.name)).readAll()); + logDebug("Hashed file contents from %s", NativePath(file.name).head); } } auto hash = sha1.finish(); @@ -711,7 +715,7 @@ newlist ~= entry; } - Path path = m_repositories[type].packagePath; + NativePath path = m_repositories[type].packagePath; if( !existsDirectory(path) ) mkdirRecurse(path.toNativeString()); writeJsonFile(path ~ LocalPackagesFilename, Json(newlist)); } @@ -745,7 +749,7 @@ Package sp; if (spr.path.length) { - auto p = Path(spr.path); + auto p = NativePath(spr.path); p.normalize(); enforce(!p.absolute, "Sub package paths must be sub paths of the parent package."); auto path = pack.path ~ p; @@ -753,7 +757,7 @@ logError("Package %s declared a sub-package, definition file is missing: %s", pack.name, path.toNativeString()); continue; } - sp = Package.load(path, Path.init, pack); + sp = Package.load(path, NativePath.init, pack); } else sp = new Package(spr.recipe, pack.path, pack); // Add the subpackage. @@ -772,7 +776,7 @@ string package_; Dependency version_; Version targetVersion; - Path targetPath; + NativePath targetPath; this(string package_, Dependency version_, Version target_version) { @@ -781,7 +785,7 @@ this.targetVersion = target_version; } - this(string package_, Dependency version_, Path target_path) + this(string package_, Dependency version_, NativePath target_path) { this.package_ = package_; this.version_ = version_; @@ -799,13 +803,13 @@ private struct Repository { - Path path; - Path packagePath; - Path[] searchPath; + NativePath path; + NativePath packagePath; + NativePath[] searchPath; Package[] localPackages; PackageOverride[] overrides; - this(Path path) + this(NativePath path) { this.path = path; this.packagePath = path ~"packages/"; diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index 449cbd0..44ddbcf 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -59,7 +59,7 @@ pre_release: If true, matches the latest pre-release version. Otherwise prefers stable versions. */ - void fetchPackage(Path path, string package_id, Dependency dep, bool pre_release); + void fetchPackage(NativePath path, string package_id, Dependency dep, bool pre_release); /** Retrieves only the recipe of a particular package. @@ -88,10 +88,10 @@ */ class FileSystemPackageSupplier : PackageSupplier { private { - Path m_path; + NativePath m_path; } - this(Path root) { m_path = root; } + this(NativePath root) { m_path = root; } override @property string description() { return "file repository at "~m_path.toNativeString(); } @@ -99,7 +99,7 @@ { Version[] ret; foreach (DirEntry d; dirEntries(m_path.toNativeString(), package_id~"*", SpanMode.shallow)) { - Path p = Path(d.name); + NativePath p = NativePath(d.name); logDebug("Entry: %s", p); enforce(to!string(p.head)[$-4..$] == ".zip"); auto vers = p.head.toString()[package_id.length+1..$-4]; @@ -110,7 +110,7 @@ return ret; } - void fetchPackage(Path path, string packageId, Dependency dep, bool pre_release) + void fetchPackage(NativePath path, string packageId, Dependency dep, bool pre_release) { enforce(path.absolute); logInfo("Storing package '"~packageId~"', version requirements: %s", dep); @@ -131,9 +131,9 @@ return null; } - private Path bestPackageFile(string packageId, Dependency dep, bool pre_release) + private NativePath bestPackageFile(string packageId, Dependency dep, bool pre_release) { - Path toPath(Version ver) { + NativePath toPath(Version ver) { return m_path ~ (packageId ~ "-" ~ ver.toString() ~ ".zip"); } auto versions = getVersions(packageId).filter!(v => dep.matches(v)).array; @@ -183,14 +183,14 @@ return ret; } - void fetchPackage(Path path, string packageId, Dependency dep, bool pre_release) + void fetchPackage(NativePath path, string packageId, Dependency dep, bool pre_release) { import std.array : replace; Json best = getBestPackage(packageId, dep, pre_release); if (best.type == Json.Type.null_) return; auto vers = best["version"].get!string; - auto url = m_registryUrl ~ Path(PackagesPath~"/"~packageId~"/"~vers~".zip"); + auto url = m_registryUrl ~ NativePath(PackagesPath~"/"~packageId~"/"~vers~".zip"); logDiagnostic("Downloading from '%s'", url); foreach(i; 0..3) { try{ @@ -222,7 +222,7 @@ m_metadataCache.remove(packageId); } - auto url = m_registryUrl ~ Path(PackagesPath ~ "/" ~ packageId ~ ".json"); + auto url = m_registryUrl ~ NativePath(PackagesPath ~ "/" ~ packageId ~ ".json"); logDebug("Downloading metadata for %s", packageId); logDebug("Getting from %s", url); @@ -292,22 +292,24 @@ package abstract class AbstractFallbackPackageSupplier : PackageSupplier { - protected PackageSupplier m_default, m_fallback; + protected PackageSupplier m_default; + protected PackageSupplier[] m_fallbacks; - this(PackageSupplier default_, PackageSupplier fallback) + this(PackageSupplier default_, PackageSupplier[] fallbacks) { m_default = default_; - m_fallback = fallback; + m_fallbacks = fallbacks; } override @property string description() { - return format("%s (fallback %s)", m_default.description, m_fallback.description); + import std.algorithm : map; + return format("%s (fallback %s)", m_default.description, m_fallbacks.map!(x => x.description)); } // Workaround https://issues.dlang.org/show_bug.cgi?id=2525 abstract override Version[] getVersions(string package_id); - abstract override void fetchPackage(Path path, string package_id, Dependency dep, bool pre_release); + abstract override void fetchPackage(NativePath path, string package_id, Dependency dep, bool pre_release); abstract override Json fetchPackageRecipe(string package_id, Dependency dep, bool pre_release); abstract override SearchResult[] searchPackages(string query); } @@ -322,7 +324,19 @@ private template fallback(T, alias func) { enum fallback = q{ - scope (failure) return m_fallback.%1$s(args); + import std.range : back, dropBackOne; + import dub.internal.vibecompat.core.log : logDebug; + scope (failure) + { + foreach (m_fallback; m_fallbacks.dropBackOne) + { + try + return m_fallback.%1$s(args); + catch(Exception) + logDebug("Package supplier %s failed. Trying next fallback.", m_fallback); + } + return m_fallbacks.back.%1$s(args); + } return m_default.%1$s(args); }.format(__traits(identifier, func)); } diff --git a/source/dub/project.d b/source/dub/project.d index 23a3a96..8e23c84 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -56,7 +56,7 @@ project_path = Path of the root package to load pack = An existing `Package` instance to use as the root package */ - this(PackageManager package_manager, Path project_path) + this(PackageManager package_manager, NativePath project_path) { Package pack; auto packageFile = Package.findPackageFile(project_path); @@ -262,8 +262,40 @@ d.name, SelectedVersions.defaultFile); } + // search for orphan sub configurations + void warnSubConfig(string pack, string config) { + logWarn("The sub configuration directive \"%s\" -> \"%s\" " + ~ "references a package that is not specified as a dependency " + ~ "and will have no effect.", pack, config); + } + void checkSubConfig(string pack, string config) { + auto p = getDependency(pack, true); + if (p && !p.configurations.canFind(config)) { + logWarn("The sub configuration directive \"%s\" -> \"%s\" " + ~ "references a configuration that does not exist.", + pack, config); + } + } + auto globalbs = m_rootPackage.getBuildSettings(); + foreach (p, c; globalbs.subConfigurations) { + if (p !in globalbs.dependencies) warnSubConfig(p, c); + else checkSubConfig(p, c); + } + foreach (c; m_rootPackage.configurations) { + auto bs = m_rootPackage.getBuildSettings(c); + foreach (p, c; bs.subConfigurations) { + if (p !in bs.dependencies && p !in globalbs.dependencies) + warnSubConfig(p, c); + else checkSubConfig(p, c); + } + } + + // check for version specification mismatches bool[Package] visited; void validateDependenciesRec(Package pack) { + // perform basic package linting + pack.simpleLint(); + foreach (d; pack.getAllDependencies()) { auto basename = getBasePackageName(d.name); if (m_selections.hasSelectedVersion(basename)) { @@ -274,7 +306,7 @@ } } - auto deppack = getDependency(name, true); + auto deppack = getDependency(d.name, true); if (deppack in visited) continue; visited[deppack] = true; if (deppack) validateDependenciesRec(deppack); @@ -324,7 +356,7 @@ else { auto path = vspec.path; if (!path.absolute) path = m_rootPackage.path ~ path; - p = m_packageManager.getOrLoadPackage(path, Path.init, true); + p = m_packageManager.getOrLoadPackage(path, NativePath.init, true); if (subname.length) p = m_packageManager.getSubPackage(p, subname, true); } } else if (m_dependencies.canFind!(d => getBasePackageName(d.name) == basename)) { @@ -339,10 +371,10 @@ } if (!p && !vspec.path.empty) { - Path path = vspec.path; + NativePath path = vspec.path; if (!path.absolute) path = pack.path ~ path; logDiagnostic("%sAdding local %s in %s", indent, dep.name, path); - p = m_packageManager.getOrLoadPackage(path, Path.init, true); + p = m_packageManager.getOrLoadPackage(path, NativePath.init, true); if (p.parentPackage !is null) { logWarn("%sSub package %s must be referenced using the path to it's parent package.", indent, dep.name); p = p.parentPackage; @@ -1204,7 +1236,7 @@ var = vres.data; } if (is_path) { - auto p = Path(var); + auto p = NativePath(var); if (!p.absolute) { return (pack.path ~ p).toNativeString(); } else return p.toNativeString(); @@ -1267,7 +1299,7 @@ /** Constructs a new version selections from an existing JSON file. */ - this(Path path) + this(NativePath path) { auto json = jsonFromFile(path); deserialize(json); @@ -1310,7 +1342,7 @@ } /// Selects a certain path for a specific package. - void selectVersion(string package_id, Path path) + void selectVersion(string package_id, NativePath path) { if (auto ps = package_id in m_selections) { if (ps.dep == Dependency(path)) @@ -1352,7 +1384,7 @@ should be used as the file name and the directory should be the root directory of the project's root package. */ - void save(Path path) + void save(NativePath path) { Json json = serialize(); auto file = openFile(path, FileMode.createTrunc); @@ -1391,7 +1423,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(NativePath(j["path"].get!string)); else throw new Exception(format("Unexpected type for dependency: %s", j.type)); } diff --git a/source/dub/recipe/io.d b/source/dub/recipe/io.d index 52dbefe..c66072e 100644 --- a/source/dub/recipe/io.d +++ b/source/dub/recipe/io.d @@ -16,7 +16,7 @@ The file format (JSON/SDLang) will be determined from the file extension. Params: - filename = Path of the package recipe file + filename = NativePath of the package recipe file parent_name = Optional name of the parent package (if this is a sub package) Returns: Returns the package recipe contents @@ -24,10 +24,10 @@ */ PackageRecipe readPackageRecipe(string filename, string parent_name = null) { - return readPackageRecipe(Path(filename), parent_name); + return readPackageRecipe(NativePath(filename), parent_name); } /// ditto -PackageRecipe readPackageRecipe(Path filename, string parent_name = null) +PackageRecipe readPackageRecipe(NativePath filename, string parent_name = null) { import dub.internal.utils : stripUTF8Bom; import dub.internal.vibecompat.core.file : openFile, FileMode; @@ -137,7 +137,7 @@ } /// ditto -void writePackageRecipe(Path filename, in ref PackageRecipe recipe) +void writePackageRecipe(NativePath filename, in ref PackageRecipe recipe) { writePackageRecipe(filename.toNativeString, recipe); } diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index e76d487..016b5dd 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -162,7 +162,7 @@ /// Constructs a BuildSettings object from this template. - void getPlatformSettings(ref BuildSettings dst, in BuildPlatform platform, Path base_path) + void getPlatformSettings(ref BuildSettings dst, in BuildPlatform platform, NativePath base_path) const { dst.targetType = this.targetType; if (!this.targetPath.empty) dst.targetPath = this.targetPath; @@ -183,7 +183,7 @@ foreach (spath; paths) { enforce(!spath.empty, "Paths must not be empty strings."); - auto path = Path(spath); + auto path = NativePath(spath); if (!path.absolute) path = base_path ~ path; if (!existsFile(path) || !isDir(path.toNativeString())) { logWarn("Invalid source/import path: %s", path.toNativeString()); @@ -193,7 +193,7 @@ foreach (d; dirEntries(path.toNativeString(), pattern, SpanMode.depth)) { import std.path : baseName; if (baseName(d.name)[0] == '.' || d.isDir) continue; - auto src = Path(d.name).relativeTo(base_path); + auto src = NativePath(d.name).relativeTo(base_path); files ~= src.toNativeString(); } } diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d index 0e2bab6..33dc59d 100644 --- a/source/dub/recipe/sdl.d +++ b/source/dub/recipe/sdl.d @@ -177,7 +177,7 @@ if ("version" in attrs) logDiagnostic("Ignoring version specification (%s) for path based dependency %s", attrs["version"][0].value.get!string, attrs["path"][0].value.get!string); dep.versionSpec = "*"; - dep.path = Path(attrs["path"][0].value.get!string); + dep.path = NativePath(attrs["path"][0].value.get!string); } else { enforceSDL("version" in attrs, "Missing version specification.", t); dep.versionSpec = attrs["version"][0].value.get!string; @@ -230,7 +230,7 @@ foreach (pack, d; bs.dependencies) { Attribute[] attribs; - if (d.path.length) attribs ~= new Attribute(null, "path", Value(d.path.toString())); + if (!d.path.empty) attribs ~= new Attribute(null, "path", Value(d.path.toString())); else attribs ~= new Attribute(null, "version", Value(d.versionSpec)); if (d.optional) attribs ~= new Attribute(null, "optional", Value(true)); ret ~= new Tag(null, "dependency", [Value(pack)], attribs); @@ -439,7 +439,7 @@ assert(rec.ddoxTool == "ddoxtool"); assert(rec.buildSettings.dependencies.length == 2); assert(rec.buildSettings.dependencies["projectname:subpackage1"].optional == false); - assert(rec.buildSettings.dependencies["projectname:subpackage1"].path == Path(".")); + assert(rec.buildSettings.dependencies["projectname:subpackage1"].path == NativePath(".")); assert(rec.buildSettings.dependencies["somedep"].versionSpec == "1.0.0"); assert(rec.buildSettings.dependencies["somedep"].optional == true); assert(rec.buildSettings.dependencies["somedep"].path.empty); diff --git a/source/dub/semver.d b/source/dub/semver.d index ac8f6e7..dd68102 100644 --- a/source/dub/semver.d +++ b/source/dub/semver.d @@ -26,7 +26,7 @@ Validates a version string according to the SemVer specification. */ bool isValidVersion(string ver) -{ +pure @nogc { // NOTE: this is not by spec, but to ensure sane input if (ver.length > 256) return false; @@ -102,7 +102,7 @@ /** Determines if a given valid SemVer version has a pre-release suffix. */ -bool isPreReleaseVersion(string ver) +bool isPreReleaseVersion(string ver) pure @nogc in { assert(isValidVersion(ver)); } body { foreach (i; 0 .. 2) { @@ -136,14 +136,14 @@ equal, and a positive number otherwise. */ int compareVersions(string a, string b) -{ +pure @nogc { // compare a.b.c numerically if (auto ret = compareNumber(a, b)) return ret; assert(a[0] == '.' && b[0] == '.'); - a.popFront(); b.popFront(); + a = a[1 .. $]; b = b[1 .. $]; if (auto ret = compareNumber(a, b)) return ret; assert(a[0] == '.' && b[0] == '.'); - a.popFront(); b.popFront(); + a = a[1 .. $]; b = b[1 .. $]; if (auto ret = compareNumber(a, b)) return ret; // give precedence to non-prerelease versions @@ -154,7 +154,7 @@ // compare the prerelease tail lexicographically do { - a.popFront(); b.popFront(); + a = a[1 .. $]; b = b[1 .. $]; if (auto ret = compareIdentifier(a, b)) return ret; } while (a.length > 0 && b.length > 0 && a[0] != '+' && b[0] != '+'); @@ -225,7 +225,8 @@ See_Also: `expandVersion` */ -string bumpVersion(string ver) { +string bumpVersion(string ver) +pure { // Cut off metadata and prerelease information. auto mi = ver.indexOfAny("+-"); if (mi > 0) ver = ver[0..mi]; @@ -258,7 +259,8 @@ See_Also: `bumpVersion` */ -string expandVersion(string ver) { +string expandVersion(string ver) +pure { auto mi = ver.indexOfAny("+-"); auto sub = ""; if (mi > 0) { @@ -282,18 +284,18 @@ } private int compareIdentifier(ref string a, ref string b) -{ +pure @nogc { bool anumber = true; bool bnumber = true; bool aempty = true, bempty = true; int res = 0; while (true) { - if (a.front != b.front && res == 0) res = a.front - b.front; - if (anumber && (a.front < '0' || a.front > '9')) anumber = false; - if (bnumber && (b.front < '0' || b.front > '9')) bnumber = false; - a.popFront(); b.popFront(); - aempty = a.empty || a.front == '.' || a.front == '+'; - bempty = b.empty || b.front == '.' || b.front == '+'; + if (a[0] != b[0] && res == 0) res = a[0] - b[0]; + if (anumber && (a[0] < '0' || a[0] > '9')) anumber = false; + if (bnumber && (b[0] < '0' || b[0] > '9')) bnumber = false; + a = a[1 .. $]; b = b[1 .. $]; + aempty = !a.length || a[0] == '.' || a[0] == '+'; + bempty = !b.length || b[0] == '.' || b[0] == '+'; if (aempty || bempty) break; } @@ -312,20 +314,20 @@ } private int compareNumber(ref string a, ref string b) -{ +pure @nogc { int res = 0; while (true) { - if (a.front != b.front && res == 0) res = a.front - b.front; - a.popFront(); b.popFront(); - auto aempty = a.empty || (a.front < '0' || a.front > '9'); - auto bempty = b.empty || (b.front < '0' || b.front > '9'); + if (a[0] != b[0] && res == 0) res = a[0] - b[0]; + a = a[1 .. $]; b = b[1 .. $]; + auto aempty = !a.length || (a[0] < '0' || a[0] > '9'); + auto bempty = !b.length || (b[0] < '0' || b[0] > '9'); if (aempty != bempty) return bempty - aempty; if (aempty) return res; } } private bool isValidIdentifierChain(string str, bool allow_leading_zeros = false) -{ +pure @nogc { if (str.length == 0) return false; while (str.length) { auto end = str.indexOf('.'); @@ -338,7 +340,7 @@ } private bool isValidIdentifier(string str, bool allow_leading_zeros = false) -{ +pure @nogc { if (str.length < 1) return false; bool numeric = true; @@ -361,7 +363,7 @@ } private bool isValidNumber(string str) -{ +pure @nogc { if (str.length < 1) return false; foreach (ch; str) if (ch < '0' || ch > '9') @@ -374,7 +376,7 @@ } private sizediff_t indexOfAny(string str, in char[] chars) -{ +pure @nogc { sizediff_t ret = -1; foreach (ch; chars) { auto idx = str.indexOf(ch); diff --git a/test/issue1194-warn-wrong-subconfig.sh b/test/issue1194-warn-wrong-subconfig.sh new file mode 100755 index 0000000..414bfe2 --- /dev/null +++ b/test/issue1194-warn-wrong-subconfig.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +OUTPUT=`${DUB} build --root ${CURR_DIR}/issue1194-warn-wrong-subconfig 2>&1 || true` + +# make sure the proper errors occur in the output +echo $OUTPUT | grep -c "sub configuration directive \"bar\" -> \"baz\" references a package that is not specified as a dependency" > /dev/null +echo $OUTPUT | grep -c "sub configuration directive \"staticlib-simple\" -> \"foo\" references a configuration that does not exist" > /dev/null +! echo $OUTPUT | grep -c "sub configuration directive \"sourcelib-simple\" -> \"library\" references a package that is not specified as a dependency" > /dev/null +! echo $OUTPUT | grep -c "sub configuration directive \"sourcelib-simple\" -> \"library\" references a configuration that does not exist" > /dev/null + +# make sure no bogs warnings are issued for packages with no sub configuration directives +OUTPUT=`${DUB} build --root ${CURR_DIR}/1-exec-simple 2>&1` +! echo $OUTPUT | grep -c "sub configuration directive.*references" > /dev/null diff --git a/test/issue1194-warn-wrong-subconfig/.no_build b/test/issue1194-warn-wrong-subconfig/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue1194-warn-wrong-subconfig/.no_build diff --git a/test/issue1194-warn-wrong-subconfig/dub.sdl b/test/issue1194-warn-wrong-subconfig/dub.sdl new file mode 100644 index 0000000..867864d --- /dev/null +++ b/test/issue1194-warn-wrong-subconfig/dub.sdl @@ -0,0 +1,8 @@ +name "test" +dependency "staticlib-simple" path="../1-staticLib-simple" +dependency "sourcelib-simple" path="../1-sourceLib-simple" +targetType "executable" + +subConfiguration "staticlib-simple" "foo" +subConfiguration "bar" "baz" +subConfiguration "sourcelib-simple" "library" diff --git a/test/issue1194-warn-wrong-subconfig/source/app.d b/test/issue1194-warn-wrong-subconfig/source/app.d new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/test/issue1194-warn-wrong-subconfig/source/app.d @@ -0,0 +1 @@ +void main() {} diff --git a/test/issue895-local-configuration.sh b/test/issue895-local-configuration.sh index d00bb62..9799288 100755 --- a/test/issue895-local-configuration.sh +++ b/test/issue895-local-configuration.sh @@ -1,11 +1,6 @@ #!/usr/bin/env bash . $(dirname "${BASH_SOURCE[0]}")/common.sh -cd ${CURR_DIR} -mkdir ../etc -mkdir ../etc/dub -echo "{\"defaultCompiler\": \"foo\"}" > ../etc/dub/settings.json - if [ -e /var/lib/dub/settings.json ]; then die $LINENO 'Found existing system wide DUB configuration. Aborting.' fi @@ -14,9 +9,68 @@ die $LINENO 'Found existing user wide DUB configuration. Aborting.' fi -if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF 'Unknown compiler: foo'; then - rm -r ../etc - die $LINENO 'DUB did not find the local configuration' +cd ${CURR_DIR} +mkdir -p ../etc/dub +echo "{\"defaultCompiler\": \"foo\"}" > ../etc/dub/settings.json +echo "Empty file named foo." > ../bin/foo + +function cleanup { + rm -r ../etc +} + +trap cleanup EXIT + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Unknown compiler: $(dirname $CURR_DIR)/bin/foo"; then + rm ../bin/foo + die $LINENO 'DUB did not find the local configuration with an adjacent compiler.' fi -rm -r ../etc +echo "{\"defaultCompiler\": \"$CURR_DIR/foo\"}" > ../etc/dub/settings.json +mv ../bin/foo $CURR_DIR + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Unknown compiler: $CURR_DIR/foo"; then + rm $CURR_DIR/foo + die $LINENO 'DUB did not find a locally-configured compiler with an absolute path.' +fi + +echo "{\"defaultCompiler\": \"~/.dub/foo\"}" > ../etc/dub/settings.json +mv $CURR_DIR/foo ~/.dub/ + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Unknown compiler: "; then + rm ~/.dub/foo + die $LINENO 'DUB did not find a locally-configured compiler with a tilde-prefixed path.' +fi + +echo "{\"defaultCompiler\": \"\$DUB_BINARY_PATH/../foo\"}" > ../etc/dub/settings.json +mv ~/.dub/foo .. + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Unknown compiler: $(dirname $CURR_DIR)/bin/../foo"; then + rm ../foo + die $LINENO 'DUB did not find a locally-configured compiler with a DUB-relative path.' +fi + +echo "{\"defaultCompiler\": \"../foo\"}" > ../etc/dub/settings.json + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "defaultCompiler specified in a DUB config file cannot use an unqualified relative path"; then + rm ../foo + die $LINENO 'DUB did not error properly for a locally-configured compiler with a relative path.' +fi + +rm ../etc/dub/settings.json +echo "Empty file named ldc2." > ../bin/ldc2 + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Failed to invoke the compiler $(dirname $CURR_DIR)/bin/ldc2 to determine the build platform"; then + rm ../bin/ldc2 + die $LINENO 'DUB did not find ldc2 adjacent to it.' +fi + +echo "{\"defaultCompiler\": \"foo\"}" > ../etc/dub/settings.json +rm ../bin/ldc2 +export PATH=$(dirname $CURR_DIR)${PATH:+:$PATH} + +if ! { ${DUB} describe --single issue103-single-file-package.d 2>&1 || true; } | grep -cF "Unknown compiler: foo"; then + rm ../foo + die $LINENO 'DUB did not find a locally-configured compiler in its PATH.' +fi + +rm ../foo diff --git a/travis-ci.sh b/travis-ci.sh index f36816c..c483599 100755 --- a/travis-ci.sh +++ b/travis-ci.sh @@ -4,7 +4,7 @@ source ~/dlang/*/activate # activate host compiler -if [ -z "$FRONTEND" -o "$FRONTEND" \> 2.068.z ]; then +if [ -z "$FRONTEND" -o "$FRONTEND" \> 2.071.z ]; then vibe_ver=$(jq -r '.versions | .["vibe-d"]' < dub.selections.json) dub fetch vibe-d --version=$vibe_ver # get optional dependency dub test --compiler=${DC} -c library-nonet