diff --git a/source/dub/commandline.d b/source/dub/commandline.d index e62e533..c6b5228 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -440,8 +440,8 @@ if (warn_missing_package) { bool found = existsFile(dub.rootPath ~ "source/app.d"); if (!found) - foreach (f; packageInfoFilenames) - if (existsFile(dub.rootPath ~ f)) { + foreach (f; packageInfoFiles) + if (existsFile(dub.rootPath ~ f.filename)) { found = true; break; } diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 0ca456d..570bc8b 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -327,7 +327,7 @@ allfiles ~= buildsettings.stringImportFiles; // TODO: add library files foreach (p; packages) - allfiles ~= (p.packageInfoFile != Path.init ? p : p.basePackage).packageInfoFile.toNativeString(); + allfiles ~= (p.packageInfoFilename != Path.init ? p : p.basePackage).packageInfoFilename.toNativeString(); foreach (f; additional_dep_files) allfiles ~= f.toNativeString(); if (main_pack is m_project.rootPackage) allfiles ~= (main_pack.path ~ SelectedVersions.defaultFile).toNativeString(); diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index e96d66c..21fa661 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -184,8 +184,8 @@ } foreach (p; targets[packname].packages) - if (!p.packageInfoFile.empty) - addFile(p.packageInfoFile.toNativeString(), false); + if (!p.packageInfoFilename.empty) + addFile(p.packageInfoFilename.toNativeString(), false); if (files.targetType == TargetType.staticLibrary) foreach(s; files.sourceFiles.filter!(s => !isLinkerFile(s))) addFile(s, true); diff --git a/source/dub/init.d b/source/dub/init.d index b1a87a1..a9e1ee2 100644 --- a/source/dub/init.d +++ b/source/dub/init.d @@ -9,7 +9,7 @@ import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.core.log; -import dub.package_ : packageInfoFilenames, defaultPackageFilename; +import dub.package_ : packageInfoFiles, defaultPackageFilename; import std.datetime; import std.exception; @@ -21,6 +21,10 @@ void initPackage(Path root_path, string type) { + void enforceDoesNotExist(string filename) { + enforce(!existsFile(root_path ~ filename), "The target directory already contains a '"~filename~"' file. Aborting."); + } + //Check to see if a target directory needs to be created if( !root_path.empty ){ if( !existsFile(root_path) ) @@ -28,9 +32,12 @@ } //Make sure we do not overwrite anything accidentally - auto files = packageInfoFilenames ~ ["source/", "views/", "public/", "dub.json", ".gitignore"]; + foreach (fil; packageInfoFiles) + enforceDoesNotExist(fil.filename); + + auto files = ["source/", "views/", "public/", "dub.json", ".gitignore"]; foreach (fil; files) - enforce(!existsFile(root_path ~ fil), "The target directory already contains a '"~fil~"' file. Aborting."); + enforceDoesNotExist(fil); switch (type) { default: throw new Exception("Unknown package init type: "~type); diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index 9613553..569c07c 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -183,7 +183,7 @@ } } -private string stripUTF8Bom(string str) +string stripUTF8Bom(string str) { if( str.length >= 3 && str[0 .. 3] == [0xEF, 0xBB, 0xBF] ) return str[3 ..$]; diff --git a/source/dub/package_.d b/source/dub/package_.d index 01da4cf..84487f6 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -25,10 +25,30 @@ import std.traits : EnumMembers; + +enum PackageFormat { json, sdl } +struct FilenameAndFormat +{ + string filename; + PackageFormat format; +} +struct PathAndFormat +{ + Path path; + PackageFormat format; + @property bool empty() { return path.empty; } + string toString() { return path.toString(); } +} + // Supported package descriptions in decreasing order of preference. -enum packageInfoFilenames = ["dub.json", /*"dub.sdl",*/ "package.json"]; +enum FilenameAndFormat[] packageInfoFiles = [ + {"dub.json", PackageFormat.json}, + /*{"dub.sdl",PackageFormat.sdl},*/ + {"package.json", PackageFormat.json} +]; + string defaultPackageFilename() { - return packageInfoFilenames[0]; + return packageInfoFiles[0].filename; } /** @@ -42,48 +62,49 @@ private { Path m_path; - Path m_infoFile; + PathAndFormat m_infoFile; PackageInfo m_info; Package m_parentPackage; Package[] m_subPackages; Path[] m_exportedPackages; } - static Path findPackageFile(Path path) + static PathAndFormat findPackageFile(Path path) { - foreach(f; packageInfoFilenames) { - auto filename = path ~ f; - if(existsFile(filename)) return filename; + foreach(file; packageInfoFiles) { + auto filename = path ~ file.filename; + if(existsFile(filename)) return PathAndFormat(filename, file.format); } - return Path(); + return PathAndFormat(Path()); } - this(Path root, Path infoFile = Path(), Package parent = null, string versionOverride = "") + this(Path root, PathAndFormat infoFile = PathAndFormat(), Package parent = null, string versionOverride = "") { - Json info; + RawPackage raw_package; m_infoFile = infoFile; try { if(m_infoFile.empty) { m_infoFile = findPackageFile(root); - if(m_infoFile.empty) throw new Exception("no package file was found, expected one of the following: "~to!string(packageInfoFilenames)); + if(m_infoFile.empty) throw new Exception("no package file was found, expected one of the following: "~to!string(packageInfoFiles)); } - info = jsonFromFile(m_infoFile); - } catch (Exception ex) throw new Exception(format("Failed to load package at %s: %s", root.toNativeString(), ex.msg)); + raw_package = rawPackageFromFile(m_infoFile); + } catch (Exception ex) throw ex;//throw new Exception(format("Failed to load package %s: %s", m_infoFile.toNativeString(), ex.msg)); - enforce(info.type != Json.Type.undefined, format("Missing package description for package at %s", root.toNativeString())); - this(info, root, parent, versionOverride); + enforce(raw_package !is null, format("Missing package description for package at %s", root.toNativeString())); + this(raw_package, root, parent, versionOverride); } - - this(Json packageInfo, Path root = Path(), Package parent = null, string versionOverride = "") + + this(Json package_info, Path root = Path(), Package parent = null, string versionOverride = "") + { + this(new JsonPackage(package_info), root, parent, versionOverride); + } + this(RawPackage raw_package, Path root = Path(), Package parent = null, string versionOverride = "") { m_parentPackage = parent; m_path = root; m_path.endsWithSlash = true; - // force the package name to be lower case - packageInfo.name = packageInfo.name.get!string.toLower(); - // check for default string import folders foreach(defvf; ["views"]){ auto p = m_path ~ defvf; @@ -92,7 +113,7 @@ } string app_main_file; - auto pkg_name = packageInfo.name.get!string(); + auto pkg_name = (raw_package is null) ? "unknown" : raw_package.package_name; // check for default source folders foreach(defsf; ["source/", "src/"]){ @@ -108,10 +129,11 @@ } } - // parse the JSON description + // parse the Package description + if(raw_package !is null) { scope(failure) logError("Failed to parse package description in %s", root.toNativeString()); - m_info.parseJson(packageInfo, parent ? parent.name : null); + raw_package.parseInto(m_info, parent ? parent.name : null); if (!versionOverride.empty) m_info.version_ = versionOverride; @@ -157,7 +179,7 @@ } // load all sub packages defined in the package description - foreach (sub; packageInfo.subPackages.opt!(Json[])) { + foreach (sub; m_info.subPackages.opt!(Json[])) { enforce(!m_parentPackage, format("'subPackages' found in '%s'. This is only supported in the main package file for '%s'.", name, m_parentPackage.name)); if (sub.type == Json.Type.string) { @@ -166,9 +188,9 @@ enforce(!p.absolute, "Sub package paths must not be absolute: " ~ sub.get!string); enforce(!p.startsWith(Path("..")), "Sub packages must be in a sub directory, not " ~ sub.get!string); m_exportedPackages ~= p; - if (!path.empty) m_subPackages ~= new Package(path ~ p, Path(), this, this.vers); + if (!path.empty) m_subPackages ~= new Package(path ~ p, PathAndFormat(), this, this.vers); } else { - m_subPackages ~= new Package(sub, root, this); + m_subPackages ~= new Package(new JsonPackage(sub), root, this); } } @@ -185,7 +207,7 @@ @property void ver(Version ver) { assert(m_parentPackage is null); m_info.version_ = ver.toString(); } @property ref inout(PackageInfo) info() inout { return m_info; } @property Path path() const { return m_path; } - @property Path packageInfoFile() const { return m_infoFile; } + @property Path packageInfoFilename() const { return m_infoFile.path; } @property const(Dependency[string]) dependencies() const { return m_info.dependencies; } @property inout(Package) basePackage() inout { return m_parentPackage ? m_parentPackage.basePackage : this; } @property inout(Package) parentPackage() inout { return m_parentPackage; } @@ -223,7 +245,7 @@ auto dstFile = openFile(filename.toNativeString(), FileMode.CreateTrunc); scope(exit) dstFile.close(); dstFile.writePrettyJsonString(m_info.toJson()); - m_infoFile = filename; + m_infoFile = PathAndFormat(filename); } inout(Package) getSubPackage(string name, bool silent_fail = false) @@ -484,7 +506,7 @@ return c; throw new Exception("Unknown configuration: "~name); } - + void parseJson(Json json, string parent_name) { foreach( string field, value; json ){ @@ -915,3 +937,51 @@ return null; } + + +private RawPackage rawPackageFromFile(PathAndFormat file, bool silent_fail = false) { + if( silent_fail && !existsFile(file.path) ) return null; + auto f = openFile(file.path.toNativeString(), FileMode.Read); + scope(exit) f.close(); + auto text = stripUTF8Bom(cast(string)f.readAll()); + + final switch(file.format) { + case PackageFormat.json: + return new JsonPackage(parseJsonString(text)); + case PackageFormat.sdl: + if(silent_fail) return null; throw new Exception("SDL not implemented"); + } +} + +private abstract class RawPackage +{ + string package_name; // Should already be lower case + string version_; + abstract void parseInto(ref PackageInfo info, string parent_name); +} +private class JsonPackage : RawPackage +{ + Json json; + this(Json json) { + this.json = json; + + string nameLower = json.name.get!string.toLower(); + this.json.name = nameLower; + this.package_name = nameLower; + + Json versionJson = json["version"]; + this.version_ = (versionJson.type == Json.Type.undefined) ? null : versionJson.get!string; + } + override void parseInto(ref PackageInfo info, string parent_name) + { + info.parseJson(json, parent_name); + } +} +private class SdlPackage : RawPackage +{ + override void parseInto(ref PackageInfo info, string parent_name) + { + throw new Exception("SDL packages not implemented yet"); + } +} + diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 82ab7e8..ae18464 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -152,7 +152,7 @@ return null; } - Package getOrLoadPackage(Path path, Path infoFile = Path()) + Package getOrLoadPackage(Path path, PathAndFormat infoFile = PathAndFormat()) { path.endsWithSlash = true; foreach (p; getPackageIterator()) @@ -312,8 +312,8 @@ Path zip_prefix; outer: foreach(ArchiveMember am; archive.directory) { auto path = Path(am.name); - foreach (fil; packageInfoFilenames) - if (path.length == 2 && path.head.toString == fil) { + foreach (fil; packageInfoFiles) + if (path.length == 2 && path.head.toString == fil.filename) { zip_prefix = path[0 .. $-1]; break outer; } @@ -355,12 +355,12 @@ logDiagnostic("%s file(s) copied.", to!string(countFiles)); // overwrite dub.json (this one includes a version field) - auto pack = new Package(destination, Path(), null, package_info["version"].get!string); + auto pack = new Package(destination, PathAndFormat(), null, package_info["version"].get!string); - if (pack.packageInfoFile.head != defaultPackageFilename()) { + if (pack.packageInfoFilename.head != defaultPackageFilename()) { // Storeinfo saved a default file, this could be different to the file from the zip. - removeFile(pack.packageInfoFile); - journal.remove(Journal.Entry(Journal.Type.RegularFile, Path(pack.packageInfoFile.head))); + removeFile(pack.packageInfoFilename); + journal.remove(Journal.Entry(Journal.Type.RegularFile, Path(pack.packageInfoFilename.head))); journal.add(Journal.Entry(Journal.Type.RegularFile, Path(defaultPackageFilename()))); } pack.storeInfo(); @@ -554,7 +554,7 @@ } if (!pp) { - Path infoFile = Package.findPackageFile(path); + auto infoFile = Package.findPackageFile(path); if (!infoFile.empty) pp = new Package(path, infoFile); else { logWarn("Locally registered package %s %s was not found. Please run \"dub remove-local %s\".", @@ -595,7 +595,7 @@ logDebug("iterating dir %s entry %s", path.toNativeString(), pdir.name); if( !pdir.isDirectory ) continue; auto pack_path = path ~ (pdir.name ~ "/"); - Path packageFile = Package.findPackageFile(pack_path); + auto packageFile = Package.findPackageFile(pack_path); if (packageFile.empty) continue; Package p; try { @@ -726,7 +726,7 @@ } // Add the subpackage. try { - dst_repos ~= new Package(path, Path(), pack); + dst_repos ~= new Package(path, PathAndFormat(), pack); } catch (Exception e) { logError("Package '%s': Failed to load sub-package in %s, error: %s", pack.name, path.toNativeString(), e.msg); logDiagnostic("Full error: %s", e.toString().sanitize()); diff --git a/source/dub/project.d b/source/dub/project.d index c9aad82..f505bd0 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -47,12 +47,10 @@ this(PackageManager package_manager, Path project_path) { Package pack; - Path packageFile = Package.findPackageFile(project_path); + auto packageFile = Package.findPackageFile(project_path); if (packageFile.empty) { logWarn("There was no package description found for the application in '%s'.", project_path.toNativeString()); - auto json = Json.emptyObject; - json.name = "unknown"; - pack = new Package(json, project_path); + pack = new Package(null, project_path); } else { pack = package_manager.getOrLoadPackage(project_path, packageFile); }