diff --git a/.gitignore b/.gitignore index e815926..d972db2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,24 @@ *.o *.obj +*.pdb *~ -.dub -.directory -dub.selections.json +# Unknown hidden files +.* +!.gitignore +!/.github +!/.editorconfig +!/.codecov.yml +# Unknown script files +/*.sh +/*.bat +/*.cmd +!/build.cmd +!/build.sh + +# dub generation files +dub.selections.json docs.json __dummy.html @@ -20,7 +33,22 @@ # Ignore files or directories created by the test suite. *.exe +*.lib *.log +/test/*/* +/test/*.* +!/test/*.d +!/test/*.d.min_frontend +!/test/*.sh +!/test/*.sh.min_frontend +!/test/*/.no_* +!/test/*/.min_frontend +!/test/*/.fail_build +!/test/*/dub.json +!/test/*/dub.sdl +!/test/*/dub.settings.json +!/test/*/source/ +!/test/*/src/ # Ignore coverage files cov/ diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 92092ad..7bb40bf 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -28,7 +28,7 @@ import std.conv; import std.encoding; import std.exception; -import std.file; +static import std.file; import std.getopt; import std.path : absolutePath, buildNormalizedPath, expandTilde, setExtension; import std.process : environment, spawnProcess, wait; @@ -173,7 +173,7 @@ if (options.root_path.empty) { - options.root_path = getcwd(); + options.root_path = std.file.getcwd(); } else { @@ -281,7 +281,7 @@ auto args = new CommandArgs([]); handler.prepareOptions(args); - assert(handler.options.root_path == getcwd()); + assert(handler.options.root_path == std.file.getcwd()); } /// It can set a custom root_path @@ -386,8 +386,6 @@ */ int runDubCommandLine(string[] args) { - import std.file : tempDir; - static string[] toSinglePackageArgs (string args0, string file, string[] trailing) { return [args0, "run", "-q", "--temp-build", "--single", file, "--"] ~ trailing; @@ -406,7 +404,7 @@ // While it probably isn't needed for all targets, it does simplify things a bit. // Question is can it be more generic? Probably not due to $TMP if ("TEMP" !in environment) - environment["TEMP"] = tempDir(); + environment["TEMP"] = std.file.tempDir(); // rdmd uses $TEMP to compute a temporary path. since cygwin substitutes backslashes // with slashes, this causes OPTLINK to fail (it thinks path segments are options) @@ -450,11 +448,11 @@ // we only consider the case where the file name is the first argument, // as the shell invocation cannot be controlled. else if (handler.getCommand(args[1]) is null && !args[1].startsWith("-")) { - if (exists(args[1])) { + if (std.file.exists(args[1])) { auto path = getTempFile("app", ".d"); - copy(args[1], path.toNativeString()); + std.file.copy(args[1], path.toNativeString()); args = toSinglePackageArgs(args[0], path.toNativeString(), args[2 .. $]); - } else if (exists(args[1].setExtension(".d"))) { + } else if (std.file.exists(args[1].setExtension(".d"))) { args = toSinglePackageArgs(args[0], args[1].setExtension(".d"), args[2 .. $]); } } @@ -498,9 +496,11 @@ import std.uni : toUpper; foreach (CommandGroup key; handler.commandGroups) { - foreach (Command command; key.commands) - { - if (levenshteinDistance(command_name, command.name) < 4) { + auto similarCommands = key.commands.filter!(cmd => levenshteinDistance(command_name, cmd.name) < 4).array(); + if (similarCommands) { + sort!((a, b) => levenshteinDistance(command_name, a.name) < levenshteinDistance( + command_name, b.name))(similarCommands); + foreach (command; similarCommands) { logInfo("Did you mean '%s'?", command.name); } } @@ -565,7 +565,7 @@ string root_path, recipeFile; enum Color { automatic, on, off } Color colorMode = Color.automatic; - SkipPackageSuppliers skipRegistry = SkipPackageSuppliers.none; + SkipPackageSuppliers skipRegistry = SkipPackageSuppliers.default_; PlacementLocation placementLocation = PlacementLocation.user; deprecated("Use `Color` instead, the previous naming was a limitation of error message formatting") @@ -591,6 +591,30 @@ ~ "', supported values: --color[=auto], --color=always, --color=never"); } + private void parseSkipRegistry(string option, string value) @safe + { + // We only want to support `none`, `standard`, `configured`, and `all`. + // We use a separate function to prevent getopt from parsing SkipPackageSuppliers.default_. + assert(option == "skip-registry", + "parseSkipRegistry called with unknown option '" ~ option ~ "'"); + switch (value) with (SkipPackageSuppliers) { + case "none": + skipRegistry = none; + break; + case "standard": + skipRegistry = standard; + break; + case "configured": + skipRegistry = configured; + break; + case "all": + skipRegistry = all; + break; + default: + throw new GetOptException("skip-registry only accepts 'none', 'standard', 'configured', and 'all', not '" ~ value ~ "'"); + } + } + /// Parses all common options and stores the result in the struct instance. void prepare(CommandArgs args) { @@ -602,7 +626,7 @@ " DUB: URL to DUB registry (default)", " Maven: URL to Maven repository + group id containing dub packages as artifacts. E.g. mvn+http://localhost:8040/maven/libs-release/dubpackages", ]); - args.getopt("skip-registry", &skipRegistry, [ + args.getopt("skip-registry", &skipRegistry, &parseSkipRegistry, [ "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. "~defaultRegistryURLs[0]~")", @@ -2844,7 +2868,7 @@ logInfo("Starting", Color.light_green, "Executing dustmite..."); auto testcmd = appender!string(); testcmd.formattedWrite("%s dustmite --test-package=%s --build=%s --config=%s", - thisExePath, prj.name, this.baseSettings.buildType, this.baseSettings.config); + std.file.thisExePath, prj.name, this.baseSettings.buildType, this.baseSettings.config); if (m_compilerName.length) testcmd.formattedWrite(" \"--compiler=%s\"", m_compilerName); if (m_arch.length) testcmd.formattedWrite(" --arch=%s", m_arch); diff --git a/source/dub/data/settings.d b/source/dub/data/settings.d index 85531e0..20c6672 100644 --- a/source/dub/data/settings.d +++ b/source/dub/data/settings.d @@ -17,6 +17,7 @@ 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. + default_, /// The value wasn't specified. It is provided in order to know when it is safe to ignore it } /** @@ -39,7 +40,22 @@ @Optional string[] registryUrls; @Optional NativePath[] customCachePaths; - SetInfo!(SkipPackageSuppliers) skipRegistry; + private struct SkipRegistry { + SkipPackageSuppliers skipRegistry; + static SkipRegistry fromString (string value) { + import std.conv : to; + auto result = value.to!SkipPackageSuppliers; + if (result == SkipPackageSuppliers.default_) { + throw new Exception( + "skipRegistry value `default_` is only meant for interal use." + ~ " Instead, use one of `none`, `standard`, `configured`, or `all`." + ); + } + return SkipRegistry(result); + } + alias skipRegistry this; + } + SetInfo!(SkipRegistry) skipRegistry; SetInfo!(string) defaultCompiler; SetInfo!(string) defaultArchitecture; SetInfo!(bool) defaultLowMemory; @@ -171,3 +187,15 @@ auto m3 = Settings.init.merge(c1); assert(m3 == c1); } + +unittest { + // Test that SkipPackageRegistry.default_ is not allowed + + import dub.internal.configy.Read; + import std.exception : assertThrown; + + const str1 = `{ + "skipRegistry": "all" +`; + assertThrown!Exception(parseConfigString!Settings(str1, "/dev/null")); +} diff --git a/source/dub/dependency.d b/source/dub/dependency.d index fc444e0..ddadb84 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -99,6 +99,12 @@ this.spec = s; } + int opCmp(in typeof(this) other) @safe const { + return name == other.name + ? spec.opCmp(other.spec) + : name.opCmp(other.name); + } + /// Name of the referenced package. PackageName name; diff --git a/source/dub/dub.d b/source/dub/dub.d index 69518ab..ec8e83f 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -48,9 +48,7 @@ /// The URL to the official package registry and it's default fallback registries. static immutable string[] defaultRegistryURLs = [ "https://code.dlang.org/", - "https://codemirror.dlang.org/", - "https://dub.bytecraft.nl/", - "https://code-mirror.dlang.io/", + "https://codemirror.dlang.org/" ]; /** Returns a default list of package suppliers. @@ -159,6 +157,13 @@ init(); + if (skip == SkipPackageSuppliers.default_) { + // If unspecified on the command line, take + // the value from the configuration files, or + // default to none. + skip = m_config.skipRegistry.set ? m_config.skipRegistry.value : SkipPackageSuppliers.none; + } + const registry_var = environment.get("DUB_REGISTRY", null); m_packageSuppliers = this.makePackageSuppliers(base, skip, registry_var); m_packageManager = this.makePackageManager(); diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index d0026c9..9ff0c43 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -609,34 +609,102 @@ /** * Search for module keyword in D Code + * A primitive parser to skip comments and whitespace to get + * the module's name from the module declaration. */ string getModuleNameFromContent(string content) { - import std.regex; - import std.string; + import std.ascii: isAlpha, isAlphaNum, isWhite; + import std.algorithm: among; + import core.exception: RangeError; - content = content.strip; - if (!content.length) return null; + enum keyword = "module"; - static bool regex_initialized = false; - static Regex!char comments_pattern, module_pattern; + size_t i = 0; + size_t startIndex = 0, endIndex = 0; + auto foundKeyword = false; - if (!regex_initialized) { - comments_pattern = regex(`//[^\r\n]*\r?\n?|/\*.*?\*/|/\+.*\+/`, "gs"); - module_pattern = regex(`module\s+([\w\.]+)\s*;`, "g"); - regex_initialized = true; + auto ch() { + return content[i]; } - content = replaceAll(content, comments_pattern, " "); - auto result = matchFirst(content, module_pattern); + static bool isIdentChar(in char c) { + return !isWhite(c) && c != '/' && c != ';'; + } - if (!result.empty) return result[1]; + try { + while(i < content.length) { + if(ch == keyword[0] && content[i .. i + keyword.length] == keyword) { + // -1 because the end of the loop will advance by 1 + i += keyword.length - 1; + foundKeyword = true; + } + else if(ch == '/') { + ++i; + // line comment? + if(ch == '/') { + while(ch != '\n') + ++i; + } + // block comment? + else if(ch == '*') { + ++i; + while(ch != '*' || content[i + 1] != '/') + ++i; + ++i; // skip over closing '/' + } + // nested comment? + else if(ch == '+') { + ++i; - return null; + size_t level = 1; + + while(level > 0) { + if(ch == '/') { + ++i; + if(ch == '+') { + ++i; + ++level; + } + } + if(ch == '+') { + ++i; + if(ch == '/') { + --level; + } else continue; + } + ++i; + } + } + } + else if(isIdentChar(ch) && foundKeyword) { + if(startIndex == 0) + startIndex = i; + ++i; // skip the first char of the name + while(isIdentChar(ch)) { + ++i; + } + // when we get here, either we're at the end of the module's identifier, + // or there are comments afterwards + if(endIndex == 0) { + endIndex = i; + } + if(!isIdentChar(ch)) + return content[startIndex .. endIndex]; + else continue; + } else if(!isIdentChar(ch) && foundKeyword && startIndex != 0) { + return content[startIndex .. endIndex]; + } + ++i; + } + return ""; + } catch(RangeError) { + return ""; + } } unittest { assert(getModuleNameFromContent("") == ""); - assert(getModuleNameFromContent("module myPackage.myModule;") == "myPackage.myModule"); + assert(getModuleNameFromContent("module myPackage.myModule;") == "myPackage.myModule", getModuleNameFromContent("module myPackage.myModule;")); assert(getModuleNameFromContent("module \t\n myPackage.myModule \t\r\n;") == "myPackage.myModule"); assert(getModuleNameFromContent("// foo\nmodule bar;") == "bar"); assert(getModuleNameFromContent("/*\nfoo\n*/\nmodule bar;") == "bar"); @@ -648,11 +716,20 @@ assert(getModuleNameFromContent("/+ module foo; +/\nmodule bar;") == "bar"); assert(getModuleNameFromContent("/+ /+ module foo; +/ +/\nmodule bar;") == "bar"); assert(getModuleNameFromContent("// module foo;\nmodule bar; // module foo;") == "bar"); + assert(getModuleNameFromContent("// module foo;\nmodule// module foo;\nbar//module foo;\n;// module foo;") == "bar"); + assert(getModuleNameFromContent("/* module foo; */\nmodule/*module foo;*/bar/*module foo;*/;") == "bar", getModuleNameFromContent("/* module foo; */\nmodule/*module foo;*/bar/*module foo;*/;")); assert(getModuleNameFromContent("/+ /+ module foo; +/ module foo; +/ module bar;") == "bar"); - //assert(getModuleNameFromContent("/+ /+ module foo; +/ module foo; +/ module bar/++/;") == "bar"); // nested comments require a context-free parser! + assert(getModuleNameFromContent("/+ /+ module foo; +/ module foo; +/ module bar/++/;") == "bar"); assert(getModuleNameFromContent("/*\nmodule sometest;\n*/\n\nmodule fakemath;\n") == "fakemath"); + assert(getModuleNameFromContent("module foo_bar;") == "foo_bar"); + assert(getModuleNameFromContent("module _foo_bar;") == "_foo_bar"); + assert(getModuleNameFromContent("/++ ++/\nmodule foo;") == "foo"); + assert(getModuleNameFromContent("module pokémon;") == "pokémon"); + assert(getModuleNameFromContent("module éclair;") == "éclair"); + assert(getModuleNameFromContent("/** module foo*/ module bar;") == "bar"); + assert(getModuleNameFromContent("/* / module foo*/ module bar;") == "bar"); } /** diff --git a/source/dub/internal/vibecompat/core/file.d b/source/dub/internal/vibecompat/core/file.d index 75a94cf..1193ceb 100644 --- a/source/dub/internal/vibecompat/core/file.d +++ b/source/dub/internal/vibecompat/core/file.d @@ -252,8 +252,7 @@ */ void ensureDirectory(NativePath path) { - if (!existsDirectory(path)) - mkdirRecurse(path.toNativeString()); + mkdirRecurse(path.toNativeString()); } /** diff --git a/source/dub/package_.d b/source/dub/package_.d index 94d2597..9b25558 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -573,13 +573,18 @@ // Left as package until the final API for this has been found package auto getAllDependenciesRange() const { + import std.algorithm: sort, uniq; + import std.array: array; return chain( only(this.recipe.buildSettings.dependencies.byKeyValue), this.recipe.configurations.map!(c => c.buildSettings.dependencies.byKeyValue) ) .joiner() - .map!(d => PackageDependency(PackageName(d.key), d.value)); + .map!(d => PackageDependency(PackageName(d.key), d.value)) + .array + .sort + .uniq; } diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 90eec57..eb17a9f 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -24,10 +24,10 @@ import std.algorithm : countUntil, filter, map, sort, canFind, remove; import std.array; import std.conv; +import std.datetime.systime; import std.digest.sha; import std.encoding : sanitize; import std.exception; -import std.file; import std.range; import std.string; import std.zip; @@ -810,7 +810,7 @@ assert(!name.sub.length, "Cannot store a subpackage, use main package instead"); NativePath dstpath = this.getPackagePath(dest, name, vers.toString()); - ensureDirectory(dstpath.parentPath()); + this.ensureDirectory(dstpath.parentPath()); const lockPath = dstpath.parentPath() ~ ".lock"; // possibly wait for other dub instance @@ -861,18 +861,18 @@ else return NativePath(path.bySegment.array[zip_prefix.length .. $]); } - static void setAttributes(string path, ArchiveMember am) + void setAttributes(NativePath path, ArchiveMember am) { import std.datetime : DosFileTimeToSysTime; auto mtime = DosFileTimeToSysTime(am.time); - setTimes(path, mtime, mtime); + this.setTimes(path, mtime, mtime); if (auto attrs = am.fileAttributes) - std.file.setAttributes(path, attrs); + this.setAttributes(path, attrs); } // extract & place - ensureDirectory(destination); + this.ensureDirectory(destination); logDebug("Copying all files..."); int countFiles = 0; foreach(ArchiveMember a; archive.directory) { @@ -882,9 +882,9 @@ logDebug("Creating %s", cleanedPath); if (dst_path.endsWithSlash) { - ensureDirectory(dst_path); + this.ensureDirectory(dst_path); } else { - ensureDirectory(dst_path.parentPath); + this.ensureDirectory(dst_path.parentPath); // for symlinks on posix systems, use the symlink function to // create them. Windows default unzip doesn't handle symlinks, // so we don't need to worry about it for Windows. @@ -901,7 +901,7 @@ } this.writeFile(dst_path, archive.expand(a)); - setAttributes(dst_path.toNativeString(), a); + setAttributes(dst_path, a); symlink_exit: ++countFiles; } @@ -913,7 +913,7 @@ if (pack.recipePath.head != defaultPackageFilename) // Storeinfo saved a default file, this could be different to the file from the zip. - removeFile(pack.recipePath); + this.removeFile(pack.recipePath); pack.storeInfo(); addPackages(this.m_internal.localPackages, pack); return pack; @@ -953,6 +953,7 @@ enforce(found, "Cannot remove, package not found: '"~ pack.name ~"', path: " ~ to!string(pack.path)); logDebug("About to delete root folder for package '%s'.", pack.path); + import std.file : rmdirRecurse; rmdirRecurse(pack.path.toNativeString()); logInfo("Removed", Color.yellow, "%s %s", pack.name.color(Mode.bold), pack.version_); } @@ -1076,6 +1077,7 @@ /// .svn folders) Hash hashPackage(Package pack) { + import std.file; import dub.internal.vibecompat.core.file; string[] ignored_directories = [".git", ".dub", ".svn"]; @@ -1246,6 +1248,29 @@ static import dub.internal.vibecompat.core.file; return dub.internal.vibecompat.core.file.iterateDirectory(path); } + + /// Ditto + protected void removeFile(NativePath path) + { + static import dub.internal.vibecompat.core.file; + return dub.internal.vibecompat.core.file.removeFile(path); + } + + /// Ditto + protected void setTimes(in NativePath path, in SysTime accessTime, + in SysTime modificationTime) + { + static import std.file; + std.file.setTimes( + path.toNativeString(), accessTime, modificationTime); + } + + /// Ditto + protected void setAttributes(in NativePath path, uint attributes) + { + static import std.file; + std.file.setAttributes(path.toNativeString(), attributes); + } } deprecated(OverrideDepMsg) diff --git a/source/dub/project.d b/source/dub/project.d index 0f4ce82..50d27a3 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -724,6 +724,13 @@ void determineDependencyConfigs(in Package p, string c) { + + // below we call createConfig for the main package if + // config.length is not zero. Carry on for that case, + // otherwise we've handle the pair (p, c) already + if(haveConfig(p.name, c) && !(config.length && p.name == m_rootPackage.name && config == c)) + return; + string[][string] depconfigs; foreach (d; p.getAllDependencies()) { auto dp = getDependency(d.name.toString(), true); diff --git a/source/dub/test/base.d b/source/dub/test/base.d index 51803a6..89c0624 100644 --- a/source/dub/test/base.d +++ b/source/dub/test/base.d @@ -51,6 +51,7 @@ import std.array; public import std.algorithm; +import std.datetime.systime; import std.exception; import std.format; import std.string; @@ -383,15 +384,6 @@ ); } - // Re-introduce hidden/deprecated overloads - public alias store = PackageManager.store; - - /// Ditto - public override Package store(NativePath src, PlacementLocation dest, in PackageName name, in Version vers) - { - assert(0, "Function not implemented"); - } - /** * Re-Implementation of `gitClone`. * @@ -451,6 +443,12 @@ } /// + protected override void removeFile(NativePath path) + { + return this.fs.removeFile(path); + } + + /// protected override IterateDirDg iterateDirectory(NativePath path) { enforce(this.fs.existsDirectory(path), @@ -460,8 +458,15 @@ foreach (c; dir.children) { FileInfo fi; fi.name = c.name; - fi.size = (c.type == FSEntry.Type.Directory) ? 0 : c.content.length; - fi.isDirectory = (c.type == FSEntry.Type.Directory); + fi.timeModified = c.attributes.modification; + final switch (c.attributes.type) { + case FSEntry.Type.File: + fi.size = c.content.length; + break; + case FSEntry.Type.Directory: + fi.isDirectory = true; + break; + } if (auto res = del(fi)) return res; } @@ -469,6 +474,19 @@ } return &iterator; } + + /// Ditto + protected override void setTimes(in NativePath path, in SysTime accessTime, + in SysTime modificationTime) + { + this.fs.setTimes(path, accessTime, modificationTime); + } + + /// Ditto + protected override void setAttributes(in NativePath path, uint attributes) + { + this.fs.setAttributes(path, attributes); + } } /** @@ -540,13 +558,25 @@ public class FSEntry { /// Type of file system entry - public enum Type { + public enum Type : ubyte { Directory, File, } + /// List FSEntry attributes + protected struct Attributes { + /// The type of FSEntry, see `FSEntry.Type` + public Type type; + /// System-specific attributes for this `FSEntry` + public uint attrs; + /// Last access time + public SysTime access; + /// Last modification time + public SysTime modification; + } /// Ditto - protected Type type; + protected Attributes attributes; + /// The name of this node protected string name; /// The parent of this entry (can be null for the root) @@ -561,7 +591,7 @@ /// Creates a new FSEntry private this (FSEntry p, Type t, string n) { - this.type = t; + this.attributes.type = t; this.parent = p; this.name = n; } @@ -569,7 +599,7 @@ /// Create the root of the filesystem, only usable from this module private this () { - this.type = Type.Directory; + this.attributes.type = Type.Directory; } /// Get a direct children node, returns `null` if it can't be found @@ -619,7 +649,7 @@ auto p = this.lookup(parentPath); enforce(silent || p !is null, "No such directory: " ~ parentPath.toNativeString()); - enforce(p is null || p.type == Type.Directory, + enforce(p is null || p.attributes.type == Type.Directory, "Parent path is not a directory: " ~ parentPath.toNativeString()); return p; } @@ -656,7 +686,7 @@ else std.stdio.write('|', '-'.repeat(indent), ' ', this.name, ' '); - final switch (this.type) { + final switch (this.attributes.type) { case Type.Directory: std.stdio.writeln('(', this.children.length, " entries):"); foreach (c; this.children) @@ -715,7 +745,7 @@ if (this.parent is null) return NativePath("/"); auto thisPath = this.parent.path ~ this.name; - thisPath.endsWithSlash = (this.type == Type.Directory); + thisPath.endsWithSlash = (this.attributes.type == Type.Directory); return thisPath; } @@ -739,21 +769,21 @@ public bool existsFile (NativePath path) const scope { auto entry = this.lookup(path); - return entry !is null && entry.type == Type.File; + return entry !is null && entry.attributes.type == Type.File; } /// Checks the existence of a directory public bool existsDirectory (NativePath path) const scope { auto entry = this.lookup(path); - return entry !is null && entry.type == Type.Directory; + return entry !is null && entry.attributes.type == Type.Directory; } /// Reads a file, returns the content as `ubyte[]` public ubyte[] readFile (NativePath path) const scope { auto entry = this.lookup(path); - enforce(entry.type == Type.File, "Trying to read a directory"); + enforce(entry.attributes.type == Type.File, "Trying to read a directory"); return entry.content.dup; } @@ -763,7 +793,7 @@ import std.utf : validate; auto entry = this.lookup(path); - enforce(entry.type == Type.File, "Trying to read a directory"); + enforce(entry.attributes.type == Type.File, "Trying to read a directory"); // Ignore BOM: If it's needed for a test, add support for it. validate(cast(const(char[])) entry.content); return cast(string) entry.content.idup(); @@ -782,7 +812,7 @@ "Cannot write to directory: " ~ path.toNativeString()); if (auto file = this.lookup(path)) { // If the file already exists, override it - enforce(file.type == Type.File, + enforce(file.attributes.type == Type.File, "Trying to write to directory: " ~ path.toNativeString()); file.content = data.dup; } else { @@ -817,7 +847,7 @@ enforce(force, "removeFile: No such file: " ~ path.toNativeString()); } else { - enforce(p.children[idx].type == Type.File, + enforce(p.children[idx].attributes.type == Type.File, "removeFile called on a directory: " ~ path.toNativeString()); p.children = p.children[0 .. idx] ~ p.children[idx + 1 .. $]; } @@ -845,13 +875,33 @@ enforce(force, "removeDir: No such directory: " ~ path.toNativeString()); } else { - enforce(p.children[idx].type == Type.Directory, + enforce(p.children[idx].attributes.type == Type.Directory, "removeDir called on a file: " ~ path.toNativeString()); enforce(force || p.children[idx].children.length == 0, "removeDir called on non-empty directory: " ~ path.toNativeString()); p.children = p.children[0 .. idx] ~ p.children[idx + 1 .. $]; } } + + /// Implement `std.file.setTimes` + public void setTimes(in NativePath path, in SysTime accessTime, + in SysTime modificationTime) + { + auto e = this.lookup(path); + enforce(e !is null, + "setTimes: No such file or directory: " ~ path.toNativeString()); + e.attributes.access = accessTime; + e.attributes.modification = modificationTime; + } + + /// Implement `std.file.setAttributes` + public void setAttributes(in NativePath path, uint attributes) + { + auto e = this.lookup(path); + enforce(e !is null, + "setTimes: No such file or directory: " ~ path.toNativeString()); + e.attributes.attrs = attributes; + } } /** diff --git a/test/1-dynLib-simple/source/dynlib/app.d b/test/1-dynLib-simple/source/dynlib/app.d index 6676219..a2d3eb5 100644 --- a/test/1-dynLib-simple/source/dynlib/app.d +++ b/test/1-dynLib-simple/source/dynlib/app.d @@ -1,5 +1,10 @@ module dynlib.app; import std.stdio; +version (unittest) {} else version (Windows) version (DigitalMars) +{ + import core.sys.windows.dll; + mixin SimpleDllMain; +} export void entry() { diff --git a/test/2-dynLib-dep/.no_build_windows b/test/2-dynLib-dep/.no_build_windows new file mode 100644 index 0000000..d55a120 --- /dev/null +++ b/test/2-dynLib-dep/.no_build_windows @@ -0,0 +1 @@ +# workaround for Issue 23177 diff --git a/test/2-dynLib-with-staticLib-dep/source/dynlib/app.d b/test/2-dynLib-with-staticLib-dep/source/dynlib/app.d index 9741cae..0672f7b 100644 --- a/test/2-dynLib-with-staticLib-dep/source/dynlib/app.d +++ b/test/2-dynLib-with-staticLib-dep/source/dynlib/app.d @@ -1,6 +1,11 @@ module dynlib.app; import std.stdio; import staticlib.app; +version (unittest) {} else version (Windows) version (DigitalMars) +{ + import core.sys.windows.dll; + mixin SimpleDllMain; +} void foo() { diff --git a/test/dub_test_root.sh b/test/dub_test_root.sh index c5ce710..7c0f50b 100755 --- a/test/dub_test_root.sh +++ b/test/dub_test_root.sh @@ -7,12 +7,13 @@ TMPDIR=$(mktemp -d "$(basename "$0").XXXXXX") +pushd "$TMPDIR" function cleanup { + popd rm -rf "$TMPDIR" } trap cleanup EXIT -cd "$TMPDIR" echo 'name "foo"' > dub.sdl diff --git a/test/interactive-remove.sh b/test/interactive-remove.sh index 8919fef..dfe1a94 100755 --- a/test/interactive-remove.sh +++ b/test/interactive-remove.sh @@ -8,33 +8,35 @@ # In the meantime, in order to make it pass on developer's machines, # we need to nuke every `dub` version in the user cache... $DUB remove dub -n || true +DUBPKGPATH=${DPATH+"$DPATH/dub/packages/dub"} +DUBPKGPATH=${DUBPKGPATH:-"$HOME/.dub/packages/dub"} -$DUB fetch dub@1.9.0 && [ -d $HOME/.dub/packages/dub/1.9.0/dub ] -$DUB fetch dub@1.10.0 && [ -d $HOME/.dub/packages/dub/1.10.0/dub ] +$DUB fetch dub@1.9.0 && [ -d $DUBPKGPATH/1.9.0/dub ] +$DUB fetch dub@1.10.0 && [ -d $DUBPKGPATH/1.10.0/dub ] echo 1 | $DUB remove dub | tr -d '\n' | grep --ignore-case 'select.*1\.9\.0.*1\.10\.0.*' -if [ -d $HOME/.dub/packages/dub/1.9.0/dub ]; then +if [ -d $DUBPKGPATH/1.9.0/dub ]; then die $LINENO 'Failed to remove dub-1.9.0' fi -$DUB fetch dub@1.9.0 && [ -d $HOME/.dub/packages/dub/1.9.0/dub ] +$DUB fetch dub@1.9.0 && [ -d $DUBPKGPATH/1.9.0/dub ] # EOF aborts remove echo -xn '' | $DUB remove dub -if [ ! -d $HOME/.dub/packages/dub/1.9.0/dub ] || [ ! -d $HOME/.dub/packages/dub/1.10.0/dub ]; then +if [ ! -d $DUBPKGPATH/1.9.0/dub ] || [ ! -d $DUBPKGPATH/1.10.0/dub ]; then die $LINENO 'Aborted dub still removed a package' fi # validates input echo -e 'abc\n4\n-1\n3' | $DUB remove dub -if [ -d $HOME/.dub/packages/dub/1.9.0/dub ] || [ -d $HOME/.dub/packages/dub/1.10.0/dub ]; then +if [ -d $DUBPKGPATH/1.9.0/dub ] || [ -d $DUBPKGPATH/1.10.0/dub ]; then die $LINENO 'Failed to remove all version of dub' fi -$DUB fetch dub@1.9.0 && [ -d $HOME/.dub/packages/dub/1.9.0/dub ] -$DUB fetch dub@1.10.0 && [ -d $HOME/.dub/packages/dub/1.10.0/dub ] +$DUB fetch dub@1.9.0 && [ -d $DUBPKGPATH/1.9.0/dub ] +$DUB fetch dub@1.10.0 && [ -d $DUBPKGPATH/1.10.0/dub ] # is non-interactive with a $DUB remove dub@1.9.0 $DUB remove dub@1.10.0 -if [ -d $HOME/.dub/packages/dub/1.9.0/dub ] || [ -d $HOME/.dub/packages/dub/1.10.0/dub ]; then +if [ -d $DUBPKGPATH/1.9.0/dub ] || [ -d $DUBPKGPATH/1.10.0/dub ]; then die $LINENO 'Failed to non-interactively remove specified versions' fi diff --git a/test/issue1775/dub.json b/test/issue1775/dub.json index 5640e4a..f1273ef 100644 --- a/test/issue1775/dub.json +++ b/test/issue1775/dub.json @@ -1,5 +1,6 @@ { "name": "test", "targetName": "test-application", - "preBuildCommands": [ "[ -f issue1775.marker ]" ] + "preBuildCommands-posix": [ "[ -f issue1775.marker ]" ], + "preBuildCommands-windows": [ "if not exist issue1775.marker exit /b 1" ] } diff --git a/test/issue2086-copyfiles-subpackage-targetpath/.no_test b/test/issue2086-copyfiles-subpackage-targetpath/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue2086-copyfiles-subpackage-targetpath/.no_test diff --git a/test/issue97-targettype-none-onerecipe/.no_test b/test/issue97-targettype-none-onerecipe/.no_test new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue97-targettype-none-onerecipe/.no_test diff --git a/test/test_registry.d b/test/test_registry.d index cb9790f..6e98466 100755 --- a/test/test_registry.d +++ b/test/test_registry.d @@ -57,6 +57,6 @@ router.get("/fallback/*", folder.serveStaticFiles(new HTTPFileServerSettings("/fallback"))); router.get("/api/*", apiFileHandler("/", folder)); router.get("/fallback/api/*", apiFileHandler("/fallback/", folder)); - listenHTTP(text(":", port), router); + listenHTTP(text("localhost:", port), router); runApplication(); } diff --git a/test/version-spec.sh b/test/version-spec.sh index 053d177..b034df5 100755 --- a/test/version-spec.sh +++ b/test/version-spec.sh @@ -2,6 +2,9 @@ . $(dirname "${BASH_SOURCE[0]}")/common.sh +DUBPKGPATH=${DPATH+"$DPATH/dub/packages/dub"} +DUBPKGPATH=${DUBPKGPATH:-"$HOME/.dub/packages/dub"} + $DUB add-local "$CURR_DIR/version-spec/newfoo" $DUB add-local "$CURR_DIR/version-spec/oldfoo" @@ -43,10 +46,10 @@ $DUB remove-local "$CURR_DIR/version-spec/newfoo" $DUB remove-local "$CURR_DIR/version-spec/oldfoo" -$DUB fetch dub@1.9.0 && [ -d $HOME/.dub/packages/dub/1.9.0/dub ] -$DUB fetch dub=1.10.0 && [ -d $HOME/.dub/packages/dub/1.10.0/dub ] +$DUB fetch dub@1.9.0 && [ -d $DUBPKGPATH/1.9.0/dub ] +$DUB fetch dub=1.10.0 && [ -d $DUBPKGPATH/1.10.0/dub ] $DUB remove dub@1.9.0 $DUB remove dub=1.10.0 -if [ -d $HOME/.dub/packages/dub/1.9.0/dub ] || [ -d $HOME/.dub/packages/dub/1.10.0/dub ]; then +if [ -d $DUBPKGPATH/1.9.0/dub ] || [ -d $DUBPKGPATH/1.10.0/dub ]; then die $LINENO 'Failed to remove specified versions' fi