diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..a52495b
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,17 @@
+# Documentation: https://docs.codecov.io/docs/codecov-yaml
+
+codecov:
+ bot: dlang-bot
+
+coverage:
+ precision: 3
+ # round: down
+ # range: "70...100"
+
+ status:
+ # Learn more at https://docs.codecov.io/docs/commit-status
+ project: true
+ patch: true
+ changes: false
+
+comment: false
diff --git a/.gitignore b/.gitignore
index 15d535f..75e23c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
/bin/dub
/bin/__test__library-nonet__
/bin/__test__library__
+/bin/dub-test-library
# Ignore files or directories created by the test suite.
/test/custom-source-main-bug487/custom-source-main-bug487
diff --git a/.travis.yml b/.travis.yml
index 999c32c..4b4b302 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,12 +3,14 @@
matrix:
include:
- - d: dmd-2.072.0-b2
+ - d: dmd-2.073.0
+ env:
+ - [FRONTEND=2.073]
+ - [COVERAGE=true]
+ - d: dmd-2.072.2
env: [FRONTEND=2.072]
- d: dmd-2.071.2
- env:
- - [FRONTEND=2.071]
- - [COVERAGE=true]
+ env: [FRONTEND=2.071]
- d: dmd-2.070.2
env: [FRONTEND=2.070]
- d: dmd-2.069.2
@@ -21,7 +23,7 @@
env: [FRONTEND=2.066]
- d: dmd-2.065.0
env: [FRONTEND=2.065]
- - d: ldc-1.1.0-beta6
+ - d: ldc-1.1.0
env: [FRONTEND=2.071]
- d: ldc-1.0.0
env: [FRONTEND=2.070]
@@ -39,8 +41,6 @@
env: [FRONTEND=2.066]
- d: gdc-4.9.0
env: [FRONTEND=2.065]
- allow_failures:
- - d: ldc-1.1.0-beta6
script:
- ./travis-ci.sh
diff --git a/source/dub/compilers/buildsettings.d b/source/dub/compilers/buildsettings.d
index de2626b..9826ea1 100644
--- a/source/dub/compilers/buildsettings.d
+++ b/source/dub/compilers/buildsettings.d
@@ -149,14 +149,14 @@
// add string import files (avoids file name duplicates in addition to path duplicates)
private void addSI(ref string[] arr, in string[] vals)
{
- outer:
+ bool[string] existing;
+ foreach (v; arr) existing[Path(v).head.toString()] = true;
foreach (v; vals) {
- auto vh = Path(v).head;
- foreach (ve; arr) {
- if (Path(ve).head == vh)
- continue outer;
+ auto s = Path(v).head.toString();
+ if (s !in existing) {
+ existing[s] = true;
+ arr ~= v;
}
- arr ~= v;
}
}
diff --git a/source/dub/dependency.d b/source/dub/dependency.d
index e2330fa..fdd9fe6 100644
--- a/source/dub/dependency.d
+++ b/source/dub/dependency.d
@@ -44,6 +44,8 @@
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
+
private {
// Shortcut to create >=0.0.0
enum ANY_IDENT = "*";
@@ -632,6 +634,7 @@
Semantic Versioning Specification v2.0.0 at http://semver.org/).
*/
struct Version {
+@safe:
private {
enum MAX_VERS = "99999.0.0";
enum UNKNOWN_VERS = "unknown";
diff --git a/source/dub/description.d b/source/dub/description.d
index c4596a0..9f519cb 100644
--- a/source/dub/description.d
+++ b/source/dub/description.d
@@ -28,7 +28,7 @@
PackageDescription[] packages; /// All packages in the dependency tree
TargetDescription[] targets; /// Build targets
@ignore size_t[string] targetLookup; /// Target index by package name name
-
+
/// Targets by name
ref inout(TargetDescription) lookupTarget(string name) inout
{
diff --git a/source/dub/dub.d b/source/dub/dub.d
index d23131e..32c84bf 100644
--- a/source/dub/dub.d
+++ b/source/dub/dub.d
@@ -149,7 +149,7 @@
loading a package.
This constructor corresponds to the "--bare" option of the command line
- interface. Use
+ interface. Use
*/
this(Path override_path)
{
@@ -243,7 +243,7 @@
Single-file packages are D files that contain a package receipe comment
at their top. A recipe comment must be a nested `/+ ... +/` style
- comment, containing the virtual recipe file name and a colon, followed by the
+ comment, containing the virtual recipe file name and a colon, followed by the
recipe contents (what would normally be in dub.sdl/dub.json).
Example:
@@ -736,7 +736,7 @@
logInfo("Removing %s in %s", pack.name, pack.path.toNativeString());
if (!m_dryRun) m_packageManager.remove(pack);
}
-
+
/// Compatibility overload. Use the version without a `force_remove` argument instead.
void remove(in Package pack, bool force_remove)
{
diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d
index 14971cc..10d7dee 100644
--- a/source/dub/generators/build.d
+++ b/source/dub/generators/build.d
@@ -378,7 +378,7 @@
foreach (p; packages)
allfiles ~= (p.recipePath != Path.init ? p : p.basePackage).recipePath.toNativeString();
foreach (f; additional_dep_files) allfiles ~= f.toNativeString();
- if (main_pack is m_project.rootPackage)
+ if (main_pack is m_project.rootPackage && m_project.rootPackage.getAllDependencies().length > 0)
allfiles ~= (main_pack.path ~ SelectedVersions.defaultFile).toNativeString();
foreach (file; allfiles.data) {
diff --git a/source/dub/generators/cmake.d b/source/dub/generators/cmake.d
index fcf0b94..269c136 100644
--- a/source/dub/generators/cmake.d
+++ b/source/dub/generators/cmake.d
@@ -26,7 +26,7 @@
{
super(project);
}
-
+
override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets)
{
auto script = appender!(char[]);
@@ -34,45 +34,45 @@
bool[string] visited;
Path projectRoot = m_project.rootPackage.path;
Path cmakeListsPath = projectRoot ~ "CMakeLists.txt";
-
+
foreach(name, info; targets)
{
if(visited.get(name, false))
continue;
-
+
visited[name] = true;
name = name.sanitize;
string targetType;
string libType;
bool addTarget = true;
-
+
switch(info.buildSettings.targetType) with(TargetType)
{
case autodetect:
throw new Exception("Don't know what to do about autodetect target type");
case executable:
targetType = "executable";
-
+
break;
case dynamicLibrary:
libType = "SHARED";
-
+
goto case;
case library:
case staticLibrary:
targetType = "library";
-
+
break;
case sourceLibrary:
addTarget = false;
-
+
break;
case none:
continue;
default:
assert(false);
}
-
+
script.put("include(UseD)\n");
script.put(
"add_d_conditions(VERSION %s DEBUG %s)\n".format(
@@ -80,17 +80,17 @@
info.buildSettings.debugVersions.dup.join(" "),
)
);
-
+
foreach(directory; info.buildSettings.importPaths)
script.put("include_directories(%s)\n".format(directory.sanitizeSlashes));
-
+
if(addTarget)
{
script.put("add_%s(%s %s\n".format(targetType, name, libType));
-
+
foreach(file; info.buildSettings.sourceFiles)
script.put(" %s\n".format(file.sanitizeSlashes));
-
+
script.put(")\n");
script.put(
"target_link_libraries(%s %s %s)\n".format(
@@ -106,16 +106,16 @@
) ~ "\n"
);
}
-
+
string filename = (projectRoot ~ "%s.cmake".format(name)).toNativeString;
File file = File(filename, "w");
-
+
file.write(script.data);
file.close;
script.shrinkTo(0);
scripts.put(filename);
}
-
+
if(!cmakeListsPath.existsFile)
{
logWarn("You must use a fork of CMake which has D support for these scripts to function properly.");
@@ -123,12 +123,12 @@
logInfo("Generating default CMakeLists.txt");
script.put("cmake_minimum_required(VERSION 3.0)\n");
script.put("project(%s D)\n".format(m_project.rootPackage.name));
-
+
foreach(path; scripts.data)
script.put("include(%s)\n".format(path));
-
+
File file = File(cmakeListsPath.toNativeString, "w");
-
+
file.write(script.data);
file.close;
}
diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d
index cb82072..7b67e52 100644
--- a/source/dub/generators/generator.d
+++ b/source/dub/generators/generator.d
@@ -294,7 +294,7 @@
bool combined; // compile all in one go instead of each dependency separately
// only used for generator "build"
- bool run, force, direct, clean, rdmd, tempBuild, parallelBuild;
+ bool run, force, direct, rdmd, tempBuild, parallelBuild;
string[] runArgs;
void delegate(int status, string output) compileCallback;
void delegate(int status, string output) linkCallback;
@@ -363,7 +363,7 @@
in BuildSettings buildsettings, Path target_path, bool generate_binary)
{
import std.path : globMatch;
-
+
if (buildsettings.postGenerateCommands.length && !isRecursiveInvocation(pack.name)) {
logInfo("Running post-generate commands for %s...", pack.name);
runBuildCommands(buildsettings.postGenerateCommands, pack, proj, settings, buildsettings);
@@ -508,13 +508,12 @@
env["DUB_RUN"] = settings.run? "TRUE" : "";
env["DUB_FORCE"] = settings.force? "TRUE" : "";
env["DUB_DIRECT"] = settings.direct? "TRUE" : "";
- env["DUB_CLEAN"] = settings.clean? "TRUE" : "";
env["DUB_RDMD"] = settings.rdmd? "TRUE" : "";
env["DUB_TEMP_BUILD"] = settings.tempBuild? "TRUE" : "";
env["DUB_PARALLEL_BUILD"] = settings.parallelBuild? "TRUE" : "";
env["DUB_RUN_ARGS"] = (cast(string[])settings.runArgs).map!(escapeShellFileName).join(" ");
-
+
auto depNames = proj.dependencies.map!((a) => a.name).array();
storeRecursiveInvokations(env, proj.rootPackage.name ~ depNames);
runCommands(commands, env);
diff --git a/source/dub/generators/sublimetext.d b/source/dub/generators/sublimetext.d
index c6ebdd3..c22fcb1 100644
--- a/source/dub/generators/sublimetext.d
+++ b/source/dub/generators/sublimetext.d
@@ -34,7 +34,7 @@
{
auto buildSettings = targets[m_project.name].buildSettings;
logDebug("About to generate sublime project for %s.", m_project.rootPackage.name);
-
+
auto root = Json([
"folders": targets.byValue.map!(f => targetFolderJson(f)).array.Json,
"build_systems": buildSystems(settings.platform),
@@ -100,7 +100,7 @@
"variants": [
[
"name": "Run".Json,
- "cmd": ["dub", "run", "--build=" ~ buildType, "--arch=" ~ arch, "--compiler="~buildPlatform.compilerBinary].map!Json.array.Json,
+ "cmd": ["dub", "run", "--build=" ~ buildType, "--arch=" ~ arch, "--compiler="~buildPlatform.compilerBinary].map!Json.array.Json,
].Json
].array.Json,
]);
diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d
index a7ace29..e47da77 100644
--- a/source/dub/generators/visuald.d
+++ b/source/dub/generators/visuald.d
@@ -336,6 +336,7 @@
//case compileOnly: singlefilemode = 3; break;
}
ret.formattedWrite(" %s\n", singlefilemode);
+ ret.formattedWrite(" %s", buildsettings.dflags.canFind("-m32mscoff") ? "1" : "0");
ret.put(" 0\n");
ret.put(" 0\n");
ret.put(" 0\n");
@@ -524,7 +525,7 @@
{
switch(architecture) {
default: logWarn("Unsupported platform('%s'), defaulting to x86", architecture); goto case;
- case "x86": return "Win32";
+ case "x86", "x86_mscoff": return "Win32";
case "x86_64": return "x64";
}
}
diff --git a/source/dub/internal/libInputVisitor.d b/source/dub/internal/libInputVisitor.d
index ba091b0..d388b78 100644
--- a/source/dub/internal/libInputVisitor.d
+++ b/source/dub/internal/libInputVisitor.d
@@ -12,17 +12,17 @@
To Public License, Version 2, as published by Sam Hocevar. See
http://www.wtfpl.net/ for more details.
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- Version 2, December 2004
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
-Copyright (C) 2004 Sam Hocevar
+Copyright (C) 2004 Sam Hocevar
-Everyone is permitted to copy and distribute verbatim or modified
-copies of this license document, and changing it is allowed as long
-as the name is changed.
+Everyone is permitted to copy and distribute verbatim or modified
+copies of this license document, and changing it is allowed as long
+as the name is changed.
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
+/
@@ -50,7 +50,7 @@
{
obj.visit(this);
}
-
+
private void ensureStarted()
{
if(!started)
@@ -59,7 +59,7 @@
started = true;
}
}
-
+
// Member 'front' must be a function due to DMD Issue #5403
private Elem _front;
@property Elem front()
@@ -67,19 +67,19 @@
ensureStarted();
return _front;
}
-
+
void popFront()
{
ensureStarted();
call();
}
-
+
@property bool empty()
{
ensureStarted();
return state == Fiber.State.TERM;
}
-
+
void yield(Elem elem)
{
_front = elem;
diff --git a/source/dub/internal/sdlang/ast.d b/source/dub/internal/sdlang/ast.d
index cf4f0e0..cf0776c 100644
--- a/source/dub/internal/sdlang/ast.d
+++ b/source/dub/internal/sdlang/ast.d
@@ -27,7 +27,7 @@
{
Value value;
Location location;
-
+
private Tag _parent;
/// Get parent tag. To set a parent, attach this Attribute to its intended
/// parent tag by calling 'Tag.add(...)', or by passing it to
@@ -62,7 +62,7 @@
else
_namespace = value;
}
-
+
private string _name;
/// Not including namespace. Use 'fullName' if you want the namespace included.
@property string name()
@@ -75,7 +75,7 @@
if(_parent && _name != value)
{
_parent.updateId++;
-
+
void removeFromGroupedLookup(string ns)
{
// Remove from _parent._attributes[ns]
@@ -83,14 +83,14 @@
auto targetIndex = sameNameAttrs.countUntil(this);
_parent._attributes[ns][_name].removeIndex(targetIndex);
}
-
+
// Remove from _parent._tags
removeFromGroupedLookup(_namespace);
removeFromGroupedLookup("*");
// Change name
_name = value;
-
+
// Add to new locations in _parent._attributes
_parent._attributes[_namespace][_name] ~= this;
_parent._attributes["*"][_name] ~= this;
@@ -111,7 +111,7 @@
this.location = location;
this.value = value;
}
-
+
this(string name, Value value, Location location = Location(0, 0, 0))
{
this._namespace = "";
@@ -119,14 +119,14 @@
this.location = location;
this.value = value;
}
-
+
/// Removes 'this' from its parent, if any. Returns 'this' for chaining.
/// Inefficient ATM, but it works.
Attribute remove()
{
if(!_parent)
return this;
-
+
void removeFromGroupedLookup(string ns)
{
// Remove from _parent._attributes[ns]
@@ -134,7 +134,7 @@
auto targetIndex = sameNameAttrs.countUntil(this);
_parent._attributes[ns][_name].removeIndex(targetIndex);
}
-
+
// Remove from _parent._attributes
removeFromGroupedLookup(_namespace);
removeFromGroupedLookup("*");
@@ -147,13 +147,13 @@
auto sameNamespaceAttrs = _parent.attributeIndicies[_namespace];
auto attrIndiciesIndex = sameNamespaceAttrs.countUntil(allAttrsIndex);
_parent.attributeIndicies[_namespace].removeIndex(attrIndiciesIndex);
-
+
// Fixup other indicies
foreach(ns, ref nsAttrIndicies; _parent.attributeIndicies)
foreach(k, ref v; nsAttrIndicies)
if(v > allAttrsIndex)
v--;
-
+
_parent.removeNamespaceIfEmpty(_namespace);
_parent.updateId++;
_parent = null;
@@ -171,7 +171,7 @@
_name == a._name &&
value == a.value;
}
-
+
string toSDLString()()
{
Appender!string sink;
@@ -232,7 +232,7 @@
else
_namespace = value;
}
-
+
private string _name;
/// Not including namespace. Use 'fullName' if you want the namespace included.
@property string name()
@@ -245,7 +245,7 @@
if(_parent && _name != value)
{
_parent.updateId++;
-
+
void removeFromGroupedLookup(string ns)
{
// Remove from _parent._tags[ns]
@@ -253,14 +253,14 @@
auto targetIndex = sameNameTags.countUntil(this);
_parent._tags[ns][_name].removeIndex(targetIndex);
}
-
+
// Remove from _parent._tags
removeFromGroupedLookup(_namespace);
removeFromGroupedLookup("*");
-
+
// Change name
_name = value;
-
+
// Add to new locations in _parent._tags
_parent._tags[_namespace][_name] ~= this;
_parent._tags["*"][_name] ~= this;
@@ -268,7 +268,7 @@
else
_name = value;
}
-
+
/// This tag's name, including namespace if one exists.
@property string fullName()
{
@@ -279,7 +279,7 @@
// could invalidate existing ranges. This way, the ranges can detect when
// they've been invalidated.
private size_t updateId=0;
-
+
this(Tag parent = null)
{
if(parent)
@@ -304,7 +304,7 @@
if(parent)
parent.add(this);
-
+
this.values = values;
this.add(attributes);
this.add(children);
@@ -319,7 +319,7 @@
private Attribute[][string][string] _attributes; // attributes[namespace or "*"][name][i]
private Tag[][string][string] _tags; // tags[namespace or "*"][name][i]
-
+
/// Adds a Value, Attribute, Tag (or array of such) as a member/child of this Tag.
/// Returns 'this' for chaining.
/// Throws 'SDLangValidationException' if trying to add an Attribute or Tag
@@ -330,7 +330,7 @@
updateId++;
return this;
}
-
+
///ditto
Tag add(Value[] vals)
{
@@ -339,7 +339,7 @@
return this;
}
-
+
///ditto
Tag add(Attribute attr)
{
@@ -350,12 +350,12 @@
"Use Attribute.remove() before adding it to another tag."
);
}
-
+
if(!allNamespaces.canFind(attr._namespace))
allNamespaces ~= attr._namespace;
attr._parent = this;
-
+
allAttributes ~= attr;
attributeIndicies[attr._namespace] ~= allAttributes.length-1;
_attributes[attr._namespace][attr._name] ~= attr;
@@ -364,7 +364,7 @@
updateId++;
return this;
}
-
+
///ditto
Tag add(Attribute[] attrs)
{
@@ -373,7 +373,7 @@
return this;
}
-
+
///ditto
Tag add(Tag tag)
{
@@ -387,18 +387,18 @@
if(!allNamespaces.canFind(tag._namespace))
allNamespaces ~= tag._namespace;
-
+
tag._parent = this;
allTags ~= tag;
tagIndicies[tag._namespace] ~= allTags.length-1;
_tags[tag._namespace][tag._name] ~= tag;
_tags["*"] [tag._name] ~= tag;
-
+
updateId++;
return this;
}
-
+
///ditto
Tag add(Tag[] tags)
{
@@ -407,14 +407,14 @@
return this;
}
-
+
/// Removes 'this' from its parent, if any. Returns 'this' for chaining.
/// Inefficient ATM, but it works.
Tag remove()
{
if(!_parent)
return this;
-
+
void removeFromGroupedLookup(string ns)
{
// Remove from _parent._tags[ns]
@@ -422,7 +422,7 @@
auto targetIndex = sameNameTags.countUntil(this);
_parent._tags[ns][_name].removeIndex(targetIndex);
}
-
+
// Remove from _parent._tags
removeFromGroupedLookup(_namespace);
removeFromGroupedLookup("*");
@@ -435,19 +435,19 @@
auto sameNamespaceTags = _parent.tagIndicies[_namespace];
auto tagIndiciesIndex = sameNamespaceTags.countUntil(allTagsIndex);
_parent.tagIndicies[_namespace].removeIndex(tagIndiciesIndex);
-
+
// Fixup other indicies
foreach(ns, ref nsTagIndicies; _parent.tagIndicies)
foreach(k, ref v; nsTagIndicies)
if(v > allTagsIndex)
v--;
-
+
_parent.removeNamespaceIfEmpty(_namespace);
_parent.updateId++;
_parent = null;
return this;
}
-
+
private void removeNamespaceIfEmpty(string namespace)
{
// If namespace has no attributes, remove it from attributeIndicies/_attributes
@@ -463,7 +463,7 @@
tagIndicies.remove(namespace);
_tags.remove(namespace);
}
-
+
// If namespace is now empty, remove it from allNamespaces
if(
namespace !in tagIndicies &&
@@ -474,7 +474,7 @@
allNamespaces = allNamespaces[0..allNamespacesIndex] ~ allNamespaces[allNamespacesIndex+1..$];
}
}
-
+
struct NamedMemberRange(T, string membersGrouped)
{
private Tag tag;
@@ -498,7 +498,7 @@
else
endIndex = 0;
}
-
+
invariant()
{
assert(
@@ -511,7 +511,7 @@
{
return frontIndex == endIndex;
}
-
+
private size_t frontIndex;
@property T front()
{
@@ -537,13 +537,13 @@
endIndex--;
}
-
+
alias length opDollar;
@property size_t length()
{
return endIndex - frontIndex;
}
-
+
@property typeof(this) save()
{
auto r = typeof(this)(this.tag, this.namespace, this.name, this.updateId);
@@ -551,25 +551,25 @@
r.endIndex = this.endIndex;
return r;
}
-
+
typeof(this) opSlice()
{
return save();
}
-
+
typeof(this) opSlice(size_t start, size_t end)
{
auto r = save();
r.frontIndex = this.frontIndex + start;
r.endIndex = this.frontIndex + end;
-
+
if(
r.frontIndex > this.endIndex ||
r.endIndex > this.endIndex ||
r.frontIndex > r.endIndex
)
throw new SDLangRangeException("Slice out of range");
-
+
return r;
}
@@ -604,10 +604,10 @@
initialEndIndex = mixin("tag."~memberIndicies~"[namespace].length");
else
initialEndIndex = 0;
-
+
endIndex = initialEndIndex;
}
-
+
invariant()
{
assert(
@@ -620,7 +620,7 @@
{
return frontIndex == endIndex;
}
-
+
private size_t frontIndex;
@property T front()
{
@@ -646,13 +646,13 @@
endIndex--;
}
-
+
alias length opDollar;
@property size_t length()
{
return endIndex - frontIndex;
}
-
+
@property typeof(this) save()
{
auto r = typeof(this)(this.tag, this.namespace, this.isMaybe);
@@ -662,28 +662,28 @@
r.updateId = this.updateId;
return r;
}
-
+
typeof(this) opSlice()
{
return save();
}
-
+
typeof(this) opSlice(size_t start, size_t end)
{
auto r = save();
r.frontIndex = this.frontIndex + start;
r.endIndex = this.frontIndex + end;
-
+
if(
r.frontIndex > this.endIndex ||
r.endIndex > this.endIndex ||
r.frontIndex > r.endIndex
)
throw new SDLangRangeException("Slice out of range");
-
+
return r;
}
-
+
T opIndex(size_t index)
{
if(empty)
@@ -694,7 +694,7 @@
else
return mixin("tag."~allMembers~"[ tag."~memberIndicies~"[namespace][frontIndex+index] ]");
}
-
+
alias NamedMemberRange!(T,membersGrouped) ThisNamedMemberRange;
ThisNamedMemberRange opIndex(string name)
{
@@ -707,10 +707,10 @@
"range and that you aren't using a slice of the range."
);
}
-
+
if(!isMaybe && empty)
throw new SDLangRangeException("Range is empty");
-
+
if(!isMaybe && name !in this)
throw new SDLangRangeException(`No such `~T.stringof~` named: "`~name~`"`);
@@ -728,10 +728,10 @@
"range and that you aren't using a slice of the range."
);
}
-
+
return
namespace in mixin("tag."~membersGrouped) &&
- name in mixin("tag."~membersGrouped~"[namespace]") &&
+ name in mixin("tag."~membersGrouped~"[namespace]") &&
mixin("tag."~membersGrouped~"[namespace][name].length") > 0;
}
}
@@ -758,12 +758,12 @@
"This range has been invalidated by a change to the tag."
);
}
-
+
@property bool empty()
{
return frontIndex == endIndex;
}
-
+
private size_t frontIndex;
@property NamespaceAccess front()
{
@@ -773,7 +773,7 @@
{
if(empty)
throw new SDLangRangeException("Range is empty");
-
+
frontIndex++;
}
@@ -786,16 +786,16 @@
{
if(empty)
throw new SDLangRangeException("Range is empty");
-
+
endIndex--;
}
-
+
alias length opDollar;
@property size_t length()
{
return endIndex - frontIndex;
}
-
+
@property NamespaceRange save()
{
auto r = NamespaceRange(this.tag, this.isMaybe);
@@ -804,28 +804,28 @@
r.updateId = this.updateId;
return r;
}
-
+
typeof(this) opSlice()
{
return save();
}
-
+
typeof(this) opSlice(size_t start, size_t end)
{
auto r = save();
r.frontIndex = this.frontIndex + start;
r.endIndex = this.frontIndex + end;
-
+
if(
r.frontIndex > this.endIndex ||
r.endIndex > this.endIndex ||
r.frontIndex > r.endIndex
)
throw new SDLangRangeException("Slice out of range");
-
+
return r;
}
-
+
NamespaceAccess opIndex(size_t index)
{
if(empty)
@@ -838,22 +838,22 @@
TagRange(tag, namespace, isMaybe)
);
}
-
+
NamespaceAccess opIndex(string namespace)
{
if(!isMaybe && empty)
throw new SDLangRangeException("Range is empty");
-
+
if(!isMaybe && namespace !in this)
throw new SDLangRangeException(`No such namespace: "`~namespace~`"`);
-
+
return NamespaceAccess(
namespace,
AttributeRange(tag, namespace, isMaybe),
TagRange(tag, namespace, isMaybe)
);
}
-
+
/// Inefficient when range is a slice or has used popFront/popBack, but it works.
bool opBinaryRight(string op)(string namespace) if(op=="in")
{
@@ -893,7 +893,7 @@
{
return TagRange(this, "", false);
}
-
+
/// Access all namespaces in this tag, and the attributes/tags within them.
@property NamespaceRange namespaces()
{
@@ -926,7 +926,7 @@
{
return TagRange(tag, "", true);
}
-
+
/// Access all namespaces in this tag, and the attributes/tags within them.
@property NamespaceRange namespaces()
{
@@ -944,7 +944,7 @@
);
}
}
-
+
/// Access 'attributes', 'tags', 'namespaces' and 'all' like normal,
/// except that looking up a non-existant name/namespace with
/// opIndex(string) results in an empty array instead of a thrown SDLangRangeException.
@@ -952,13 +952,13 @@
{
return MaybeAccess(this);
}
-
+
override bool opEquals(Object o)
{
auto t = cast(Tag)o;
if(!t)
return false;
-
+
if(_namespace != t._namespace || _name != t._name)
return false;
@@ -969,7 +969,7 @@
allTags .length != t.allTags .length
)
return false;
-
+
if(values != t.values)
return false;
@@ -978,12 +978,12 @@
if(allAttributes != t.allAttributes)
return false;
-
+
// Ok because cycles are not allowed
//TODO: Actually check for or prevent cycles.
return allTags == t.allTags;
}
-
+
/// Treats 'this' as the root tag. Note that root tags cannot have
/// values or attributes, and cannot be part of a namespace.
/// If this isn't a valid root tag, 'SDLangValidationException' will be thrown.
@@ -993,7 +993,7 @@
toSDLDocument(sink, indent, indentLevel);
return sink.data;
}
-
+
///ditto
void toSDLDocument(Sink)(ref Sink sink, string indent="\t", int indentLevel=0)
if(isOutputRange!(Sink,char))
@@ -1006,11 +1006,11 @@
if(_namespace != "")
throw new SDLangValidationException("Root tags cannot have a namespace.");
-
+
foreach(tag; allTags)
tag.toSDLString(sink, indent, indentLevel);
}
-
+
/// Output this entire tag in SDL format. Does *not* treat 'this' as
/// a root tag. If you intend this to be the root of a standard SDL
/// document, use 'toSDLDocument' instead.
@@ -1020,21 +1020,21 @@
toSDLString(sink, indent, indentLevel);
return sink.data;
}
-
+
///ditto
void toSDLString(Sink)(ref Sink sink, string indent="\t", int indentLevel=0)
if(isOutputRange!(Sink,char))
{
if(_name == "" && values.length == 0)
throw new SDLangValidationException("Anonymous tags must have at least one value.");
-
+
if(_name == "" && _namespace != "")
throw new SDLangValidationException("Anonymous tags cannot have a namespace.");
-
+
// Indent
foreach(i; 0..indentLevel)
sink.put(indent);
-
+
// Name
if(_namespace != "")
{
@@ -1042,24 +1042,24 @@
sink.put(':');
}
sink.put(_name);
-
+
// Values
foreach(i, v; values)
{
// Omit the first space for anonymous tags
if(_name != "" || i > 0)
sink.put(' ');
-
+
v.toSDLString(sink);
}
-
+
// Attributes
foreach(attr; allAttributes)
{
sink.put(' ');
attr.toSDLString(sink);
}
-
+
// Child tags
bool foundChild=false;
foreach(tag; allTags)
@@ -1089,7 +1089,7 @@
import std.algorithm : sort;
Appender!string buf;
-
+
buf.put("\n");
buf.put("Tag ");
if(_namespace != "")
@@ -1113,21 +1113,21 @@
string namespaceStr;
if(attr._namespace != "")
namespaceStr = "["~attr._namespace~"]";
-
+
buf.put(
" %s%s(%s): %s\n".format(
namespaceStr, attr._name, .toString(attr.value.type), attr.value
)
);
}
-
+
// Children
foreach(tagNamespace; _tags.keys.sort())
if(tagNamespace != "*")
foreach(tagName; _tags[tagNamespace].keys.sort())
foreach(tag; _tags[tagNamespace][tagName])
buf.put( tag.toDebugString().replace("\n", "\n ") );
-
+
return buf.data;
}
}
@@ -1147,14 +1147,14 @@
assert(range.empty);
return;
}
-
+
static bool defaultEquals(E e1, E e2)
{
return e1 == e2;
}
if(equals is null)
equals = &defaultEquals;
-
+
assert(equals(range.front, expected[0]));
assert(equals(range.front, expected[0])); // Ensure consistent result from '.front'
assert(equals(range.front, expected[0])); // Ensure consistent result from '.front'
@@ -1162,20 +1162,20 @@
assert(equals(range.back, expected[$-1]));
assert(equals(range.back, expected[$-1])); // Ensure consistent result from '.back'
assert(equals(range.back, expected[$-1])); // Ensure consistent result from '.back'
-
+
// Forward iteration
auto original = range.save;
auto r2 = range.save;
foreach(i; 0..expected.length)
{
//trace("Forward iteration: ", i);
-
+
// Test length/empty
assert(range.length == expected.length - i);
assert(range.length == r2.length);
assert(!range.empty);
assert(!r2.empty);
-
+
// Test front
assert(equals(range.front, expected[i]));
assert(equals(range.front, r2.front));
@@ -1201,7 +1201,7 @@
assert(range.empty);
assert(r2.empty);
assert(original.length == expected.length);
-
+
// Backwards iteration
range = original.save;
r2 = original.save;
@@ -1214,7 +1214,7 @@
assert(range.length == r2.length);
assert(!range.empty);
assert(!r2.empty);
-
+
// Test front
assert(equals(range.front, expected[0]));
assert(equals(range.front, r2.front));
@@ -1240,7 +1240,7 @@
assert(range.empty);
assert(r2.empty);
assert(original.length == expected.length);
-
+
// Random access
range = original.save;
r2 = original.save;
@@ -1253,7 +1253,7 @@
assert(range.length == r2.length);
assert(!range.empty);
assert(!r2.empty);
-
+
// Test front
assert(equals(range.front, expected[0]));
assert(equals(range.front, r2.front));
@@ -1278,13 +1278,13 @@
import sdlang.parser;
writeln("Unittesting sdlang ast...");
stdout.flush();
-
+
Tag root;
root = parseSource("");
testRandomAccessRange(root.attributes, cast( Attribute[])[]);
testRandomAccessRange(root.tags, cast( Tag[])[]);
testRandomAccessRange(root.namespaces, cast(Tag.NamespaceAccess[])[]);
-
+
root = parseSource(`
blue 3 "Lee" isThree=true
blue 5 "Chan" 12345 isThree=false
@@ -1293,7 +1293,7 @@
stuff:triangle data:points=3 data:dimensions=2
nothing
namespaces small:A=1 med:A=2 big:A=3 small:B=10 big:B=30
-
+
people visitor:a=1 b=2 {
chiyo "Small" "Flies?" nemesis="Car" score=100
yukari
@@ -1407,7 +1407,7 @@
],
[chiyo, yukari, sana, tomo, hayama]
);
-
+
assert(blue3 .opEquals( blue3 ));
assert(blue5 .opEquals( blue5 ));
assert(orange .opEquals( orange ));
@@ -1421,19 +1421,19 @@
assert(sana .opEquals( sana ));
assert(tomo .opEquals( tomo ));
assert(hayama .opEquals( hayama ));
-
+
assert(!blue3.opEquals(orange));
assert(!blue3.opEquals(people));
assert(!blue3.opEquals(sana));
assert(!blue3.opEquals(blue5));
assert(!blue5.opEquals(blue3));
-
+
alias Tag.NamespaceAccess NSA;
static bool namespaceEquals(NSA n1, NSA n2)
{
return n1.name == n2.name;
}
-
+
testRandomAccessRange(root.attributes, cast(Attribute[])[]);
testRandomAccessRange(root.tags, [blue3, blue5, nothing, namespaces, people]);
testRandomAccessRange(root.namespaces, [NSA(""), NSA("stuff")], &namespaceEquals);
@@ -1480,7 +1480,7 @@
assertThrown!SDLangRangeException(root.all.tags["foobar"]);
assertThrown!SDLangRangeException(root.attributes["foobar"]);
assertThrown!SDLangRangeException(root.all.attributes["foobar"]);
-
+
// DMD Issue #12585 causes a segfault in these two tests when using 2.064 or 2.065,
// so work around it.
//assertThrown!SDLangRangeException(root.namespaces["foobar"].tags["foobar"]);
@@ -1491,7 +1491,7 @@
catch(SDLangRangeException e)
didCatch = true;
assert(didCatch);
-
+
didCatch = false;
try
auto x = root.namespaces["foobar"].attributes["foobar"];
@@ -1520,19 +1520,19 @@
testRandomAccessRange(blue3.namespaces, [NSA("")], &namespaceEquals);
testRandomAccessRange(blue3.all.attributes, [ new Attribute("isThree", Value(true)) ]);
testRandomAccessRange(blue3.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(blue5.attributes, [ new Attribute("isThree", Value(false)) ]);
testRandomAccessRange(blue5.tags, cast(Tag[])[]);
testRandomAccessRange(blue5.namespaces, [NSA("")], &namespaceEquals);
testRandomAccessRange(blue5.all.attributes, [ new Attribute("isThree", Value(false)) ]);
testRandomAccessRange(blue5.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(orange.attributes, cast(Attribute[])[]);
testRandomAccessRange(orange.tags, cast(Tag[])[]);
testRandomAccessRange(orange.namespaces, cast(NSA[])[], &namespaceEquals);
testRandomAccessRange(orange.all.attributes, cast(Attribute[])[]);
testRandomAccessRange(orange.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(square.attributes, [
new Attribute("points", Value(4)),
new Attribute("dimensions", Value(2)),
@@ -1546,7 +1546,7 @@
new Attribute("points", Value("Still four")),
]);
testRandomAccessRange(square.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(triangle.attributes, cast(Attribute[])[]);
testRandomAccessRange(triangle.tags, cast(Tag[])[]);
testRandomAccessRange(triangle.namespaces, [NSA("data")], &namespaceEquals);
@@ -1565,13 +1565,13 @@
new Attribute("data", "dimensions", Value(2)),
]);
testRandomAccessRange(triangle.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(nothing.attributes, cast(Attribute[])[]);
testRandomAccessRange(nothing.tags, cast(Tag[])[]);
testRandomAccessRange(nothing.namespaces, cast(NSA[])[], &namespaceEquals);
testRandomAccessRange(nothing.all.attributes, cast(Attribute[])[]);
testRandomAccessRange(nothing.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(namespaces.attributes, cast(Attribute[])[]);
testRandomAccessRange(namespaces.tags, cast(Tag[])[]);
testRandomAccessRange(namespaces.namespaces, [NSA("small"), NSA("med"), NSA("big")], &namespaceEquals);
@@ -1680,19 +1680,19 @@
new Attribute("score", Value(100)),
]);
testRandomAccessRange(chiyo.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(yukari.attributes, cast(Attribute[])[]);
testRandomAccessRange(yukari.tags, cast(Tag[])[]);
testRandomAccessRange(yukari.namespaces, cast(NSA[])[], &namespaceEquals);
testRandomAccessRange(yukari.all.attributes, cast(Attribute[])[]);
testRandomAccessRange(yukari.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(sana.attributes, cast(Attribute[])[]);
testRandomAccessRange(sana.tags, cast(Tag[])[]);
testRandomAccessRange(sana.namespaces, cast(NSA[])[], &namespaceEquals);
testRandomAccessRange(sana.all.attributes, cast(Attribute[])[]);
testRandomAccessRange(sana.all.tags, cast(Tag[])[]);
-
+
testRandomAccessRange(people.attributes, [new Attribute("b", Value(2))]);
testRandomAccessRange(people.tags, [chiyo, yukari, tomo]);
testRandomAccessRange(people.namespaces, [NSA("visitor"), NSA("")], &namespaceEquals);
@@ -1712,7 +1712,7 @@
new Attribute("b", Value(2)),
]);
testRandomAccessRange(people.all.tags, [chiyo, yukari, sana, tomo, hayama]);
-
+
people.attributes["b"][0].name = "b_";
people.namespaces["visitor"].attributes["a"][0].name = "a_";
people.tags["chiyo"][0].name = "chiyo_";
@@ -1751,7 +1751,7 @@
testRandomAccessRange(people.namespaces["visitor"].tags, [sana_]);
testRandomAccessRange(people.namespaces[ ""].tags, [yukari]);
testRandomAccessRange(people.all.tags, [yukari, sana_]);
-
+
people.attributes["b_"][0].namespace = "_";
people.namespaces["visitor"].attributes["a_"][0].namespace = "visitor_";
assert("_" in people.namespaces);
@@ -1767,7 +1767,7 @@
assert(people.namespaces["_" ].attributes["b_"][0] == new Attribute("_", "b_", Value(2)));
assert(people.namespaces["visitor_"].attributes["a_"][0] == new Attribute("visitor_", "a_", Value(1)));
assert(people.namespaces["visitor_"].tags["sana_"][0] == sanaVisitor_);
-
+
people.tags["yukari"][0].remove();
people.namespaces["visitor_"].tags["sana_"][0].remove();
people.namespaces["visitor_"].attributes["a_"][0].namespace = "visitor";
@@ -1782,7 +1782,7 @@
testRandomAccessRange(people.namespaces["visitor"].tags, cast(Tag[])[]);
testRandomAccessRange(people.namespaces[ ""].tags, cast(Tag[])[]);
testRandomAccessRange(people.all.tags, cast(Tag[])[]);
-
+
people.namespaces["visitor"].attributes["a_"][0].remove();
testRandomAccessRange(people.attributes, [new Attribute("b_", Value(2))]);
testRandomAccessRange(people.namespaces, [NSA("")], &namespaceEquals);
@@ -1794,7 +1794,7 @@
testRandomAccessRange(people.all.attributes, [
new Attribute("b_", Value(2)),
]);
-
+
people.attributes["b_"][0].remove();
testRandomAccessRange(people.attributes, cast(Attribute[])[]);
testRandomAccessRange(people.namespaces, cast(NSA[])[], &namespaceEquals);
@@ -1811,7 +1811,7 @@
import sdlang.parser;
writeln("ast: Regression test issue #11...");
stdout.flush();
-
+
auto root = parseSource(
`//
a`);
@@ -1829,7 +1829,7 @@
null, "", "child",
null, null, null
);
-
+
assert("parent" in root.tags);
assert("child" !in root.tags);
testRandomAccessRange(root.tags["parent"][0].tags, [child]);
diff --git a/source/dub/internal/sdlang/lexer.d b/source/dub/internal/sdlang/lexer.d
index 4cc754b..2295ed7 100644
--- a/source/dub/internal/sdlang/lexer.d
+++ b/source/dub/internal/sdlang/lexer.d
@@ -35,7 +35,7 @@
Token[] lexSource(string source, string filename=null)
{
auto lexer = scoped!Lexer(source, filename);
-
+
// Can't use 'std.array.array(Range)' because 'lexer' is scoped
// and therefore cannot have its reference copied.
Appender!(Token[]) tokens;
@@ -98,18 +98,18 @@
private size_t posAfterLookahead; // Position after lookahead character (an index into source)
private Location tokenStart; // The starting location of the token being lexed
-
+
// Length so far of the token being lexed, not including current char
private size_t tokenLength; // Length in UTF-8 code units
private size_t tokenLength32; // Length in UTF-32 code units
-
+
// Slight kludge:
// If a numeric fragment is found after a Date (separated by arbitrary
// whitespace), it could be the "hours" part of a DateTime, or it could
// be a separate numeric literal that simply follows a plain Date. If the
// latter, then the Date must be emitted, but numeric fragment that was
// found after it needs to be saved for the the lexer's next iteration.
- //
+ //
// It's a slight kludge, and could instead be implemented as a slightly
// kludgey parser hack, but it's the only situation where SDL's lexing
// needs to lookahead more than one character, so this is good enough.
@@ -121,12 +121,12 @@
Location tokenStart;
}
private LookaheadTokenInfo lookaheadTokenInfo;
-
+
this(string source=null, string filename=null)
{
this.filename = filename;
this.source = source;
-
+
_front = Token(symbol!"Error", Location());
lookaheadTokenInfo = LookaheadTokenInfo.init;
@@ -135,14 +135,14 @@
source = source[ ByteOrderMarks[BOM.UTF8].length .. $ ];
this.source = source;
}
-
+
foreach(bom; ByteOrderMarks)
if( source.startsWith(bom) )
error(Location(filename,0,0,0), "SDL spec only supports UTF-8, not UTF-16 or UTF-32");
-
+
if(source == "")
mixin(accept!"EOF");
-
+
// Prime everything
hasNextCh = true;
nextCh = source.decode(posAfterLookahead);
@@ -150,12 +150,12 @@
location = Location(filename, 0, 0, 0);
popFront();
}
-
+
@property bool empty()
{
return _front.symbol == symbol!"EOF";
}
-
+
Token _front;
@property Token front()
{
@@ -183,12 +183,12 @@
tok.data = tokenData;
return tok;
}
-
+
private @property string tokenData()
{
return source[ tokenStart.index .. location.index ];
}
-
+
/// Check the lookahead character
private bool lookahead(dchar ch)
{
@@ -228,20 +228,20 @@
if(ch >= '0' && ch <= '9')
return true;
-
+
return ch == '+' || ch == '/' || ch == '=';
}
-
+
/// Is the current character one that's allowed
/// immediately *after* an int/float literal?
private bool isEndOfNumber()
{
if(isEOF)
return true;
-
+
return !isDigit(ch) && ch != ':' && ch != '_' && !isAlpha(ch);
}
-
+
/// Is current character the last one in an ident?
private bool isEndOfIdentCached = false;
private bool _isEndOfIdent;
@@ -253,10 +253,10 @@
_isEndOfIdent = true;
else
_isEndOfIdent = !isIdentChar(nextCh);
-
+
isEndOfIdentCached = true;
}
-
+
return _isEndOfIdent;
}
@@ -265,12 +265,12 @@
{
if(isAlpha(ch))
return true;
-
+
else if(isNumber(ch))
return true;
-
+
else
- return
+ return
ch == '-' ||
ch == '_' ||
ch == '.' ||
@@ -281,7 +281,7 @@
{
return ch >= '0' && ch <= '9';
}
-
+
private enum KeywordResult
{
Accept, // Keyword is matched
@@ -350,7 +350,7 @@
hasNextCh = false;
return;
}
-
+
nextCh = source.decode(posAfterLookahead);
isEndOfIdentCached = false;
}
@@ -370,12 +370,12 @@
if(isEOF)
mixin(accept!"EOF");
-
+
tokenStart = location;
tokenLength = 0;
tokenLength32 = 0;
isEndOfIdentCached = false;
-
+
if(lookaheadTokenInfo.exists)
{
tokenStart = lookaheadTokenInfo.tokenStart;
@@ -385,31 +385,31 @@
lexNumeric(prevLATokenInfo);
return;
}
-
+
if(ch == '=')
{
advanceChar(ErrorOnEOF.No);
mixin(accept!"=");
}
-
+
else if(ch == '{')
{
advanceChar(ErrorOnEOF.No);
mixin(accept!"{");
}
-
+
else if(ch == '}')
{
advanceChar(ErrorOnEOF.No);
mixin(accept!"}");
}
-
+
else if(ch == ':')
{
advanceChar(ErrorOnEOF.No);
mixin(accept!":");
}
-
+
else if(ch == ';')
{
advanceChar(ErrorOnEOF.No);
@@ -421,7 +421,7 @@
advanceChar(cnt, ErrorOnEOF.No);
mixin(accept!"EOL");
}
-
+
else if(isAlpha(ch) || ch == '_')
lexIdentKeyword();
@@ -430,7 +430,7 @@
else if(ch == '`')
lexRawString();
-
+
else if(ch == '\'')
lexCharacter();
@@ -451,7 +451,7 @@
private void lexIdentKeyword()
{
assert(isAlpha(ch) || ch == '_');
-
+
// Keyword
struct Key
{
@@ -471,10 +471,10 @@
keywords[4] = Key("null", Value(null ));
keywordsInited = true;
}
-
+
foreach(ref key; keywords)
key.failed = false;
-
+
auto numKeys = keywords.length;
do
@@ -486,10 +486,10 @@
{
case KeywordResult.Accept:
mixin(accept!("Value", "key.value"));
-
+
case KeywordResult.Continue:
break;
-
+
case KeywordResult.Failed:
key.failed = true;
numKeys--;
@@ -520,13 +520,13 @@
{
if(tokenLength == 0)
assert(isAlpha(ch) || ch == '_');
-
+
while(!isEOF && isIdentChar(ch))
advanceChar(ErrorOnEOF.No);
mixin(accept!"Ident");
}
-
+
/// Lex regular string
private void lexRegularString()
{
@@ -534,7 +534,7 @@
Appender!string buf;
size_t spanStart = nextPos;
-
+
// Doesn't include current character
void updateBuf()
{
@@ -543,7 +543,7 @@
buf.put( source[spanStart..location.index] );
}
-
+
advanceChar(ErrorOnEOF.Yes);
while(ch != '"')
{
@@ -564,7 +564,7 @@
default: wasEscSequence = false; break;
}
}
-
+
if(wasEscSequence)
{
advanceChar(ErrorOnEOF.Yes);
@@ -582,7 +582,7 @@
advanceChar(ErrorOnEOF.Yes);
}
-
+
updateBuf();
advanceChar(ErrorOnEOF.No); // Skip closing double-quote
mixin(accept!("Value", "buf.data"));
@@ -592,21 +592,21 @@
private void lexRawString()
{
assert(ch == '`');
-
+
do
advanceChar(ErrorOnEOF.Yes);
while(ch != '`');
-
+
advanceChar(ErrorOnEOF.No); // Skip closing back-tick
mixin(accept!("Value", "tokenData[1..$-1]"));
}
-
+
/// Lex character literal
private void lexCharacter()
{
assert(ch == '\'');
advanceChar(ErrorOnEOF.Yes); // Skip opening single-quote
-
+
dchar value;
if(ch == '\\')
{
@@ -634,25 +634,25 @@
mixin(accept!("Value", "value"));
}
-
+
/// Lex base64 binary literal
private void lexBinary()
{
assert(ch == '[');
advanceChar(ErrorOnEOF.Yes);
-
+
void eatBase64Whitespace()
{
while(!isEOF && isWhite(ch))
{
if(isNewline(ch))
advanceChar(ErrorOnEOF.Yes);
-
+
if(!isEOF && isWhite(ch))
eatWhite();
}
}
-
+
eatBase64Whitespace();
// Iterates all valid base64 characters, ending at ']'.
@@ -662,17 +662,17 @@
Lexer lexer;
private bool isInited = false;
private int numInputCharsMod4 = 0;
-
+
@property bool empty()
{
if(lexer.ch == ']')
{
if(numInputCharsMod4 != 0)
lexer.error("Length of Base64 encoding must be a multiple of 4. ("~to!string(numInputCharsMod4)~")");
-
+
return true;
}
-
+
return false;
}
@@ -680,7 +680,7 @@
{
return lexer.ch;
}
-
+
void popFront()
{
auto lex = lexer;
@@ -692,14 +692,14 @@
numInputCharsMod4++;
numInputCharsMod4 %= 4;
}
-
+
isInited = true;
}
-
+
lex.advanceChar(lex.ErrorOnEOF.Yes);
eatBase64Whitespace();
-
+
if(lex.isEOF)
lex.error("Unexpected end of file.");
@@ -707,13 +707,13 @@
{
if(!lex.isBase64(lex.ch))
lex.error("Invalid character in base64 binary literal.");
-
+
numInputCharsMod4++;
numInputCharsMod4 %= 4;
}
}
}
-
+
// This is a slow ugly hack. It's necessary because Base64.decode
// currently requires the source to have known length.
//TODO: Remove this when DMD issue #9543 is fixed.
@@ -729,7 +729,7 @@
outputBuf.put(ch);
}
}
-
+
try
//Base64.decode(Base64InputRange(this), OutputBuf());
Base64.decode(tmpBuf, OutputBuf());
@@ -737,11 +737,11 @@
//TODO: Starting with dmd 2.062, this should be a Base64Exception
catch(Exception e)
error("Invalid character in base64 binary literal.");
-
+
advanceChar(ErrorOnEOF.No); // Skip ']'
mixin(accept!("Value", "outputBuf.data"));
}
-
+
private BigInt toBigInt(bool isNegative, string absValue)
{
auto num = BigInt(absValue);
@@ -759,14 +759,14 @@
{
if(!isDigit(ch))
error("Expected a digit 0-9.");
-
+
auto spanStart = location.index;
-
+
do
{
advanceChar(ErrorOnEOF.No);
} while(!isEOF && isDigit(ch));
-
+
return source[spanStart..location.index];
}
@@ -811,7 +811,7 @@
mixin(accept!("Value", "num.toLong()"));
}
-
+
// Float (32-bit signed)?
else if(ch == 'F' || ch == 'f')
{
@@ -819,7 +819,7 @@
advanceChar(ErrorOnEOF.No);
mixin(accept!("Value", "value"));
}
-
+
// Double float (64-bit signed) with suffix?
else if((ch == 'D' || ch == 'd') && !lookahead(':')
)
@@ -828,7 +828,7 @@
advanceChar(ErrorOnEOF.No);
mixin(accept!("Value", "value"));
}
-
+
// Decimal (128+ bits signed)?
else if(
(ch == 'B' || ch == 'b') &&
@@ -840,15 +840,15 @@
advanceChar(ErrorOnEOF.No);
mixin(accept!("Value", "value"));
}
-
+
// Some floating point?
else if(ch == '.')
lexFloatingPoint(firstFragment);
-
+
// Some date?
else if(ch == '/' && hasNextCh && isDigit(nextCh))
lexDate(isNegative, firstFragment);
-
+
// Some time span?
else if(ch == ':' || ch == 'd')
lexTimeSpan(isNegative, firstFragment);
@@ -867,15 +867,15 @@
else
error("Invalid integer suffix.");
}
-
+
/// Lex any floating-point literal (after the initial numeric fragment was lexed)
private void lexFloatingPoint(string firstPart)
{
assert(ch == '.');
advanceChar(ErrorOnEOF.No);
-
+
auto secondPart = lexNumericFragment();
-
+
try
{
// Double float (64-bit signed) with suffix?
@@ -928,7 +928,7 @@
private Date makeDate(bool isNegative, string yearStr, string monthStr, string dayStr)
{
BigInt biTmp;
-
+
biTmp = BigInt(yearStr);
if(isNegative)
biTmp = -biTmp;
@@ -940,15 +940,15 @@
if(biTmp < 1 || biTmp > 12)
error(tokenStart, "Date's month is out of range.");
auto month = biTmp.toInt();
-
+
biTmp = BigInt(dayStr);
if(biTmp < 1 || biTmp > 31)
error(tokenStart, "Date's month is out of range.");
auto day = biTmp.toInt();
-
+
return Date(year, month, day);
}
-
+
private DateTimeFrac makeDateTimeFrac(
bool isNegative, Date date, string hourStr, string minuteStr,
string secondStr, string millisecondStr
@@ -960,12 +960,12 @@
if(biTmp < int.min || biTmp > int.max)
error(tokenStart, "Datetime's hour is out of range.");
auto numHours = biTmp.toInt();
-
+
biTmp = BigInt(minuteStr);
if(biTmp < 0 || biTmp > int.max)
error(tokenStart, "Datetime's minute is out of range.");
auto numMinutes = biTmp.toInt();
-
+
int numSeconds = 0;
if(secondStr != "")
{
@@ -974,7 +974,7 @@
error(tokenStart, "Datetime's second is out of range.");
numSeconds = biTmp.toInt();
}
-
+
int millisecond = 0;
if(millisecondStr != "")
{
@@ -990,7 +990,7 @@
}
Duration fracSecs = millisecond.msecs;
-
+
auto offset = hours(numHours) + minutes(numMinutes) + seconds(numSeconds);
if(isNegative)
@@ -998,7 +998,7 @@
offset = -offset;
fracSecs = -fracSecs;
}
-
+
return DateTimeFrac(DateTime(date) + offset, fracSecs);
}
@@ -1047,7 +1047,7 @@
else if(millisecondStr.length == 2)
millisecond *= 10;
}
-
+
auto duration =
dur!"days" (day) +
dur!"hours" (hour) +
@@ -1057,7 +1057,7 @@
if(isNegative)
duration = -duration;
-
+
return duration;
}
@@ -1067,7 +1067,7 @@
{
if(str.length < 2)
return Nullable!Duration(); // Unknown timezone
-
+
if(str[0] != '+' && str[0] != '-')
return Nullable!Duration(); // Unknown timezone
@@ -1085,7 +1085,7 @@
numMinutesStr = str.find(':');
numHoursStr = str[1 .. $-numMinutesStr.length];
}
-
+
long numHours = 0;
long numMinutes = 0;
bool isUnknown = false;
@@ -1133,7 +1133,7 @@
}
catch(ConvException e)
isUnknown = true;
-
+
if(isUnknown)
return Nullable!Duration(); // Unknown timezone
@@ -1144,12 +1144,12 @@
// Timezone valid
return Nullable!Duration(timeZoneOffset);
}
-
+
/// Lex date or datetime (after the initial numeric fragment was lexed)
private void lexDate(bool isDateNegative, string yearStr)
{
assert(ch == '/');
-
+
// Lex months
advanceChar(ErrorOnEOF.Yes); // Skip '/'
auto monthStr = lexNumericFragment();
@@ -1159,18 +1159,18 @@
error("Invalid date format: Missing days.");
advanceChar(ErrorOnEOF.Yes); // Skip '/'
auto dayStr = lexNumericFragment();
-
+
auto date = makeDate(isDateNegative, yearStr, monthStr, dayStr);
if(!isEndOfNumber() && ch != '/')
error("Dates cannot have suffixes.");
-
+
// Date?
if(isEOF)
mixin(accept!("Value", "date"));
-
+
auto endOfDate = location;
-
+
while(
!isEOF &&
( ch == '\\' || ch == '/' || (isWhite(ch) && !isNewline(ch)) )
@@ -1190,7 +1190,7 @@
// Date?
if(isEOF || (!isDigit(ch) && ch != '-'))
mixin(accept!("Value", "date", "", "endOfDate.index"));
-
+
auto startOfTime = location;
// Is time negative?
@@ -1200,7 +1200,7 @@
// Lex hours
auto hourStr = ch == '.'? "" : lexNumericFragment();
-
+
// Lex minutes
if(ch != ':')
{
@@ -1214,7 +1214,7 @@
}
advanceChar(ErrorOnEOF.Yes); // Skip ':'
auto minuteStr = lexNumericFragment();
-
+
// Lex seconds, if exists
string secondStr;
if(ch == ':')
@@ -1222,7 +1222,7 @@
advanceChar(ErrorOnEOF.Yes); // Skip ':'
secondStr = lexNumericFragment();
}
-
+
// Lex milliseconds, if exists
string millisecondStr;
if(ch == '.')
@@ -1232,25 +1232,25 @@
}
auto dateTimeFrac = makeDateTimeFrac(isTimeNegative, date, hourStr, minuteStr, secondStr, millisecondStr);
-
+
// Lex zone, if exists
if(ch == '-')
{
advanceChar(ErrorOnEOF.Yes); // Skip '-'
auto timezoneStart = location;
-
+
if(!isAlpha(ch))
error("Invalid timezone format.");
-
+
while(!isEOF && !isWhite(ch))
advanceChar(ErrorOnEOF.No);
-
+
auto timezoneStr = source[timezoneStart.index..location.index];
if(timezoneStr.startsWith("GMT"))
{
auto isoPart = timezoneStr["GMT".length..$];
auto offset = getTimeZoneOffset(isoPart);
-
+
if(offset.isNull())
{
// Unknown time zone
@@ -1264,7 +1264,7 @@
mixin(accept!("Value", "SysTime(dateTimeFrac.dateTime, fsecs, timezone)"));
}
}
-
+
try
{
auto timezone = PosixTimeZone.getTimeZone(timezoneStr);
@@ -1293,7 +1293,7 @@
private void lexTimeSpan(bool isNegative, string firstPart)
{
assert(ch == ':' || ch == 'd');
-
+
string dayStr = "";
string hourStr;
@@ -1324,7 +1324,7 @@
error("Invalid time span format: Missing seconds.");
advanceChar(ErrorOnEOF.Yes); // Skip ':'
auto secondStr = lexNumericFragment();
-
+
// Lex milliseconds, if exists
string millisecondStr = "";
if(ch == '.')
@@ -1335,7 +1335,7 @@
if(!isEndOfNumber())
error("Time spans cannot have suffixes.");
-
+
auto duration = makeDuration(isNegative, dayStr, hourStr, minuteStr, secondStr, millisecondStr);
mixin(accept!("Value", "duration"));
}
@@ -1354,7 +1354,7 @@
if(isEOF)
return;
-
+
Location commentStart;
State state = State.normal;
bool consumeNewlines = false;
@@ -1427,12 +1427,12 @@
}
break;
-
+
case State.lineComment:
if(lookahead(&isNewline))
state = State.normal;
break;
-
+
case State.blockComment:
if(ch == '*' && lookahead('/'))
{
@@ -1441,7 +1441,7 @@
}
break;
}
-
+
advanceChar(ErrorOnEOF.No);
if(isEOF)
{
@@ -1494,7 +1494,7 @@
if (is_same && test_locations) {
is_same = actual.map!(t => t.location).equal(expected.map!(t => t.location));
}
-
+
if(!is_same)
{
numErrors++;
@@ -1548,7 +1548,7 @@
{
writeln("Unittesting sdlang lexer...");
stdout.flush();
-
+
testLex("", []);
testLex(" ", []);
testLex("\\\n", []);
@@ -1604,7 +1604,7 @@
testLexThrows("<");
testLexThrows("*");
testLexThrows(`\`);
-
+
// Integers
testLex( "7", [ Token(symbol!"Value",loc,Value(cast( int) 7)) ]);
testLex( "-7", [ Token(symbol!"Value",loc,Value(cast( int)-7)) ]);
@@ -1624,12 +1624,12 @@
testLexThrows("7A");
testLexThrows("-A");
testLexThrows(`-""`);
-
+
testLex("7;", [
Token(symbol!"Value",loc,Value(cast(int)7)),
Token(symbol!"EOL",loc),
]);
-
+
// Floats
testLex("1.2F" , [ Token(symbol!"Value",loc,Value(cast( float)1.2)) ]);
testLex("1.2f" , [ Token(symbol!"Value",loc,Value(cast( float)1.2)) ]);
@@ -1751,7 +1751,7 @@
testLexThrows(`'\`);
testLexThrows(`'\'`);
testLexThrows("'");
-
+
// Unicode
testLex("日本語", [ Token(symbol!"Ident",loc,Value(null), "日本語") ]);
testLex("`おはよう、日本。`", [ Token(symbol!"Value",loc,Value(`おはよう、日本。`)) ]);
@@ -1941,12 +1941,12 @@
Token(symbol!"Ident",loc,Value( null),"foo."),
Token(symbol!"Value",loc,Value(cast(int)7))
]);
-
+
testLex(`
namespace:person "foo" "bar" 1 23L name.first="ひとみ" name.last="Smith" {
namespace:age 37; namespace:favorite_color "blue" // comment
somedate 2013/2/22 07:53 -- comment
-
+
inventory /* comment */ {
socks
}
@@ -2000,7 +2000,7 @@
Token(symbol!"}", loc, Value(null), "}"),
Token(symbol!"EOL", loc, Value(null), "\n"),
]);
-
+
if(numErrors > 0)
stderr.writeln(numErrors, " failed test(s)");
}
@@ -2021,7 +2021,7 @@
{
writeln("lexer: Regression test issue #11...");
stdout.flush();
-
+
void test(string input)
{
testLex(
diff --git a/source/dub/internal/sdlang/package.d b/source/dub/internal/sdlang/package.d
index c128a46..7f1c67a 100644
--- a/source/dub/internal/sdlang/package.d
+++ b/source/dub/internal/sdlang/package.d
@@ -68,7 +68,7 @@
stderr.writeln("Usage: sdlang [lex|parse|to-sdl] filename.sdl");
return 1;
}
-
+
auto filename = args[2];
try
@@ -85,7 +85,7 @@
stderr.writeln(e.msg);
return 1;
}
-
+
return 0;
}
@@ -93,28 +93,28 @@
{
auto source = cast(string)read(filename);
auto lexer = new Lexer(source, filename);
-
+
foreach(tok; lexer)
{
// Value
string value;
if(tok.symbol == symbol!"Value")
value = tok.value.hasValue? toString(tok.value.type) : "{null}";
-
+
value = value==""? "\t" : "("~value~":"~tok.value.toString()~") ";
// Data
auto data = tok.data.replace("\n", "").replace("\r", "");
if(data != "")
data = "\t|"~tok.data~"|";
-
+
// Display
writeln(
tok.location.toString, ":\t",
tok.symbol.name, value,
data
);
-
+
if(tok.symbol.name == "Error")
break;
}
diff --git a/source/dub/internal/sdlang/parser.d b/source/dub/internal/sdlang/parser.d
index 3e32690..8ccf119 100644
--- a/source/dub/internal/sdlang/parser.d
+++ b/source/dub/internal/sdlang/parser.d
@@ -173,13 +173,13 @@
private struct PullParser
{
private Lexer lexer;
-
+
private struct IDFull
{
string namespace;
string name;
}
-
+
private void error(string msg)
{
error(lexer.front.location, msg);
@@ -189,20 +189,20 @@
{
throw new SDLangParseException(loc, "Error: "~msg);
}
-
+
private InputVisitor!(PullParser, ParserEvent) v;
-
+
void visit(InputVisitor!(PullParser, ParserEvent) v)
{
this.v = v;
parseRoot();
}
-
+
private void emit(Event)(Event event)
{
v.yield( ParserEvent(event) );
}
-
+
/// ::= EOF (Lookaheads: Anything)
private void parseRoot()
{
@@ -213,11 +213,11 @@
emit( FileStartEvent(startLocation) );
parseTags();
-
+
auto token = lexer.front;
if(!token.matches!"EOF"())
error("Expected end-of-file, not " ~ token.symbol.name);
-
+
emit( FileEndEvent(token.location) );
}
@@ -283,7 +283,7 @@
parseAttributes();
parseOptChild();
parseTagTerminator();
-
+
emit( TagEndEvent() );
}
@@ -365,7 +365,7 @@
auto value = token.value;
//trace("In tag '", parent.fullName, "', found value: ", value);
emit( ValueEvent(token.location, value) );
-
+
lexer.popFront();
}
else
@@ -401,21 +401,21 @@
auto token = lexer.front;
if(!token.matches!"Ident"())
error("Expected attribute name, not "~token.symbol.name);
-
+
auto id = parseIDFull();
-
+
token = lexer.front;
if(!token.matches!"="())
error("Expected '=' after attribute name, not "~token.symbol.name);
-
+
lexer.popFront();
token = lexer.front;
if(!token.matches!"Value"())
error("Expected attribute value, not "~token.symbol.name);
-
+
//trace("In tag '", parent.fullName, "', found attribute '", attr.fullName, "'");
emit( AttributeEvent(token.location, id.namespace, id.name, token.value) );
-
+
lexer.popFront();
}
@@ -432,10 +432,10 @@
token = lexer.front;
if(!token.matches!"EOL"())
error("Expected newline or semicolon after '{', not "~token.symbol.name);
-
+
lexer.popFront();
parseTags();
-
+
token = lexer.front;
if(!token.matches!"}"())
error("Expected '}' after child tags, not "~token.symbol.name);
@@ -447,7 +447,7 @@
// Do nothing, no error.
}
}
-
+
///
/// ::= EOL (Lookahead: EOL)
/// | {empty} (Lookahead: EOF)
@@ -472,12 +472,12 @@
private struct DOMParser
{
Lexer lexer;
-
+
Tag parseRoot()
{
auto currTag = new Tag(null, null, "root");
currTag.location = Location(lexer.filename, 0, 0, 0);
-
+
auto parser = PullParser(lexer);
auto eventRange = inputVisitor!ParserEvent( parser );
foreach(event; eventRange)
@@ -486,7 +486,7 @@
{
auto newTag = new Tag(currTag, e.namespace, e.name);
newTag.location = e.location;
-
+
currTag = newTag;
}
else if(event.peek!TagEndEvent())
@@ -518,7 +518,7 @@
else
parser.error("Internal Error: Received unknown parser event");
}
-
+
return currTag;
}
}
diff --git a/source/dub/internal/sdlang/symbol.d b/source/dub/internal/sdlang/symbol.d
index 574d97f..04de244 100644
--- a/source/dub/internal/sdlang/symbol.d
+++ b/source/dub/internal/sdlang/symbol.d
@@ -50,7 +50,7 @@
{
return _name;
}
-
+
@disable this();
private this(string name)
{
diff --git a/source/dub/internal/sdlang/util.d b/source/dub/internal/sdlang/util.d
index d73b221..c2e2ac7 100644
--- a/source/dub/internal/sdlang/util.d
+++ b/source/dub/internal/sdlang/util.d
@@ -29,14 +29,14 @@
int line; /// Zero-indexed
int col; /// Zero-indexed, Tab counts as 1
size_t index; /// Index into the source
-
+
this(int line, int col, int index)
{
this.line = line;
this.col = col;
this.index = index;
}
-
+
this(string file, int line, int col, int index)
{
this.file = file;
@@ -44,7 +44,7 @@
this.col = col;
this.index = index;
}
-
+
string toString()
{
return "%s(%s:%s)".format(file, line+1, col+1);
@@ -82,7 +82,7 @@
else if(ti == typeid( Duration )) return "Duration";
else if(ti == typeid( ubyte[] )) return "ubyte[]";
else if(ti == typeid( typeof(null) )) return "null";
-
+
return "{unknown}";
}
diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d
index 83fb0ab..4de739b 100644
--- a/source/dub/internal/utils.d
+++ b/source/dub/internal/utils.d
@@ -427,43 +427,37 @@
static Regex!char comments_pattern, module_pattern;
if (!regex_initialized) {
- comments_pattern = regex(`(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)`, "g");
+ comments_pattern = regex(`//[^\r\n]*\r?\n?|/\*.*?\*/|/\+.*\+/`, "g");
module_pattern = regex(`module\s+([\w\.]+)\s*;`, "g");
regex_initialized = true;
}
- content = replaceAll(content, comments_pattern, "");
+ content = replaceAll(content, comments_pattern, " ");
auto result = matchFirst(content, module_pattern);
- string moduleName;
- if(!result.empty) moduleName = result.front;
+ if (!result.empty) return result[1];
- if (moduleName.length >= 7) moduleName = moduleName[7..$-1];
-
- return moduleName;
+ return null;
}
unittest {
- //test empty string
- string name = getModuleNameFromContent("");
- assert(name == "", "can't get module name from empty string");
-
- //test simple name
- name = getModuleNameFromContent("module myPackage.myModule;");
- assert(name == "myPackage.myModule", "can't parse module name");
-
- //test if it can ignore module inside comments
- name = getModuleNameFromContent("/**
- module fakePackage.fakeModule;
- */
- module myPackage.myModule;");
-
- assert(name == "myPackage.myModule", "can't parse module name");
-
- name = getModuleNameFromContent("//module fakePackage.fakeModule;
- module myPackage.myModule;");
-
- assert(name == "myPackage.myModule", "can't parse module name");
+ assert(getModuleNameFromContent("") == "");
+ assert(getModuleNameFromContent("module myPackage.myModule;") == "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");
+ assert(getModuleNameFromContent("/+\nfoo\n+/\nmodule bar;") == "bar");
+ assert(getModuleNameFromContent("/***\nfoo\n***/\nmodule bar;") == "bar");
+ assert(getModuleNameFromContent("/+++\nfoo\n+++/\nmodule bar;") == "bar");
+ assert(getModuleNameFromContent("// module foo;\nmodule bar;") == "bar");
+ assert(getModuleNameFromContent("/* module foo; */\nmodule bar;") == "bar");
+ 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!
}
/**
diff --git a/source/dub/internal/vibecompat/core/file.d b/source/dub/internal/vibecompat/core/file.d
index 92b79ae..0dcbe14 100644
--- a/source/dub/internal/vibecompat/core/file.d
+++ b/source/dub/internal/vibecompat/core/file.d
@@ -25,28 +25,29 @@
/* Add output range support to File
*/
struct RangeFile {
+@safe:
std.stdio.File file;
- void put(in ubyte[] bytes) { file.rawWrite(bytes); }
- void put(in char[] str) { put(cast(ubyte[])str); }
- void put(char ch) { put((&ch)[0 .. 1]); }
+ void put(in ubyte[] bytes) @trusted { file.rawWrite(bytes); }
+ void put(in char[] str) { put(cast(const(ubyte)[])str); }
+ void put(char ch) @trusted { put((&ch)[0 .. 1]); }
void put(dchar ch) { char[4] chars; put(chars[0 .. encode(chars, ch)]); }
ubyte[] readAll()
{
- auto sz = file.size;
+ auto sz = this.size;
enforce(sz <= size_t.max, "File is too big to read to memory.");
- file.seek(0, SEEK_SET);
+ () @trusted { file.seek(0, SEEK_SET); } ();
auto ret = new ubyte[cast(size_t)sz];
rawRead(ret);
return ret;
}
- void rawRead(ubyte[] dst) { enforce(file.rawRead(dst).length == dst.length, "Failed to readall bytes from file."); }
+ void rawRead(ubyte[] dst) @trusted { enforce(file.rawRead(dst).length == dst.length, "Failed to readall bytes from file."); }
void write(string str) { put(str); }
- void close() { file.close(); }
- void flush() { file.flush(); }
- @property ulong size() { return file.size; }
+ void close() @trusted { file.close(); }
+ void flush() @trusted { file.flush(); }
+ @property ulong size() @trusted { return file.size; }
}
@@ -145,8 +146,14 @@
// guess whether 2 files are identical, ignores filename and content
private bool sameFile(Path a, Path b)
{
- static assert(__traits(allMembers, FileInfo)[0] == "name");
- return getFileInfo(a).tupleof[1 .. $] == getFileInfo(b).tupleof[1 .. $];
+ version (Posix) {
+ auto st_a = std.file.DirEntry(a.toNativeString).statBuf;
+ auto st_b = std.file.DirEntry(b.toNativeString).statBuf;
+ return st_a == st_b;
+ } else {
+ static assert(__traits(allMembers, FileInfo)[0] == "name");
+ return getFileInfo(a).tupleof[1 .. $] == getFileInfo(b).tupleof[1 .. $];
+ }
}
/**
@@ -157,7 +164,7 @@
if (existsFile(to)) {
enforce(overwrite, "Destination file already exists.");
if (auto fe = collectException!FileException(removeFile(to))) {
- version (Windows) if (sameFile(from, to)) return;
+ if (sameFile(from, to)) return;
throw fe;
}
}
diff --git a/source/dub/internal/vibecompat/core/log.d b/source/dub/internal/vibecompat/core/log.d
index c0b52f3..e48569e 100644
--- a/source/dub/internal/vibecompat/core/log.d
+++ b/source/dub/internal/vibecompat/core/log.d
@@ -66,15 +66,15 @@
txt.reserve(256);
formattedWrite(txt, fmt, args);
- auto threadid = cast(ulong)cast(void*)Thread.getThis();
- auto fiberid = cast(ulong)cast(void*)Fiber.getThis();
+ auto threadid = () @trusted { return cast(ulong)cast(void*)Thread.getThis(); } ();
+ auto fiberid = () @trusted { return cast(ulong)cast(void*)Fiber.getThis(); } ();
threadid ^= threadid >> 32;
fiberid ^= fiberid >> 32;
if (level >= s_minLevel) {
File output;
- if (level == LogLevel.info) output = stdout;
- else output = stderr;
+ if (level == LogLevel.info) () @trusted { output = stdout; } ();
+ else () @trusted { output = stderr; } ();
if (output.isOpen) {
output.writeln(txt.data);
output.flush();
diff --git a/source/dub/internal/vibecompat/data/json.d b/source/dub/internal/vibecompat/data/json.d
index af9c591..4a9bc00 100644
--- a/source/dub/internal/vibecompat/data/json.d
+++ b/source/dub/internal/vibecompat/data/json.d
@@ -1743,12 +1743,12 @@
case Json.Type.null_: dst.put("null"); break;
case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break;
case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break;
- case Json.Type.float_:
+ case Json.Type.float_:
auto d = json.get!double;
- if (d != d)
+ if (d != d)
dst.put("undefined"); // JSON has no NaN value so set null
else
- formattedWrite(dst, "%.16g", json.get!double);
+ formattedWrite(dst, "%.16g", json.get!double);
break;
case Json.Type.string:
dst.put('\"');
diff --git a/source/dub/internal/vibecompat/data/serialization.d b/source/dub/internal/vibecompat/data/serialization.d
index b351324..ccbb115 100644
--- a/source/dub/internal/vibecompat/data/serialization.d
+++ b/source/dub/internal/vibecompat/data/serialization.d
@@ -184,7 +184,7 @@
string toRepresentation(T value) {
return to!string(value.x) ~ "x" ~ to!string(value.y);
}
-
+
T fromRepresentation(string value) {
string[] fields = value.split('x');
alias fieldT = typeof(T.x);
@@ -268,7 +268,7 @@
///
static if (__VERSION__ >= 2065) unittest {
import dub.internal.vibecompat.data.json;
-
+
static struct SizeI {
int x;
int y;
@@ -278,7 +278,7 @@
SizeI sizeI = deserializeWithPolicy!(JsonSerializer, SizePol, SizeI)(serializedI);
assert(sizeI.x == 1);
assert(sizeI.y == 2);
-
+
static struct SizeF {
float x;
float y;
@@ -825,22 +825,22 @@
*/
template isPolicySerializable(alias Policy, T)
{
- enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) &&
+ enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) &&
is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) == T);
}
///
unittest {
import std.conv;
-
+
// represented as a string when serialized
static struct S {
int value;
-
+
// dummy example implementations
string toString() const { return value.to!string(); }
static S fromString(string s) { return S(s.to!int()); }
}
-
+
static assert(isStringSerializable!S);
}
@@ -865,7 +865,7 @@
///
unittest {
import std.conv;
-
+
// To be represented as the boxed value when serialized
static struct Box(T) {
T value;
@@ -883,7 +883,7 @@
auto toRepresentation(S s) {
return s.value;
}
-
+
S fromRepresentation(typeof(toRepresentation(S.init)) v) {
return S(v);
}
@@ -893,7 +893,7 @@
auto toRepresentation(S s) {
return s.get();
}
-
+
S fromRepresentation(typeof(toRepresentation(S.init)) v) {
S s;
s.get() = v;
diff --git a/source/dub/internal/vibecompat/data/utils.d b/source/dub/internal/vibecompat/data/utils.d
index 7dbcf46..a967455 100644
--- a/source/dub/internal/vibecompat/data/utils.d
+++ b/source/dub/internal/vibecompat/data/utils.d
@@ -374,10 +374,10 @@
/**
TypeTuple which does not auto-expand.
-
+
Useful when you need
to multiple several type tuples as different template argument
- list parameters, without merging those.
+ list parameters, without merging those.
*/
template Group(T...)
{
@@ -407,10 +407,10 @@
version (unittest) // NOTE: GDC complains about template definitions in unittest blocks
{
import std.typetuple;
-
+
alias group = Group!(int, double, string);
alias group2 = Group!();
-
+
template Fake(T...)
{
int[] expand;
diff --git a/source/dub/internal/vibecompat/inet/path.d b/source/dub/internal/vibecompat/inet/path.d
index d2fcff8..f5bd81f 100644
--- a/source/dub/internal/vibecompat/inet/path.d
+++ b/source/dub/internal/vibecompat/inet/path.d
@@ -175,7 +175,7 @@
}
/// The last entry of the path
- @property ref immutable(PathEntry) head() const { enforce(m_nodes.length > 0); return m_nodes[$-1]; }
+ @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]; }
diff --git a/source/dub/package_.d b/source/dub/package_.d
index 5625088..c499c6e 100644
--- a/source/dub/package_.d
+++ b/source/dub/package_.d
@@ -156,7 +156,7 @@
if (recipe_file.empty) recipe_file = findPackageFile(root);
- enforce(!recipe_file.empty,
+ enforce(!recipe_file.empty,
"No package file found in %s, expected one of %s"
.format(root.toNativeString(),
packageInfoFiles.map!(f => cast(string)f.filename).join("/")));
diff --git a/source/dub/platform.d b/source/dub/platform.d
index e1df8d6..830c1d4 100644
--- a/source/dub/platform.d
+++ b/source/dub/platform.d
@@ -144,7 +144,7 @@
So the following strings are valid specifications: `"-windows-x86-dmd"`,
`"-dmd"`, `"-arm"`, `"-arm-dmd"`, `"-windows-dmd"`
-
+
Params:
platform = The build platform to match agains the platform specification
specification = The specification being matched. It must either be an
diff --git a/source/dub/project.d b/source/dub/project.d
index 2c6e493..b8f7813 100644
--- a/source/dub/project.d
+++ b/source/dub/project.d
@@ -119,7 +119,7 @@
If this function returns `false`, it may be necessary to add more entries
to `selections`, or to use `Dub.upgrade` to automatically select all
- missing dependencies.
+ missing dependencies.
*/
bool hasAllDependencies() const { return m_hasAllDependencies; }
@@ -368,7 +368,8 @@
if (!m_dependencies.canFind(p)) {
logDiagnostic("%sFound dependency %s %s", indent, dep.name, vspec.toString());
m_dependencies ~= p;
- p.warnOnSpecialCompilerFlags();
+ if (basename == m_rootPackage.basePackage.name)
+ p.warnOnSpecialCompilerFlags();
collectDependenciesRec(p, depth+1);
}
@@ -654,7 +655,7 @@
if (usedefflags) {
BuildSettings btsettings;
m_rootPackage.addBuildTypeSettings(btsettings, platform, build_type);
-
+
if (!for_root_package) {
// don't propagate unittest switch to dependencies, as dependent
// unit tests aren't run anyway and the additional code may
@@ -711,7 +712,7 @@
return listBuildSetting!attributeName(platform, getPackageConfigs(platform, config),
projectDescription, compiler, disableEscaping);
}
-
+
private string[] listBuildSetting(string attributeName)(BuildPlatform platform,
string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping)
{
@@ -720,7 +721,7 @@
else
return formatBuildSettingPlain!attributeName(platform, configs, projectDescription);
}
-
+
// Output a build setting formatted for a compiler
private string[] formatBuildSettingCompiler(string attributeName)(BuildPlatform platform,
string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping)
@@ -752,7 +753,7 @@
case "options":
auto bs = buildSettings.dup;
bs.dflags = null;
-
+
// Ensure trailing slash on directory paths
auto ensureTrailingSlash = (string path) => path.endsWith(dirSeparator) ? path : path ~ dirSeparator;
static if (attributeName == "importPaths")
@@ -770,9 +771,9 @@
bs.lflags = null;
bs.sourceFiles = null;
bs.targetType = TargetType.none; // Force Compiler to NOT omit dependency libs when package is a library.
-
+
compiler.prepareBuildSettings(bs, BuildSetting.all & ~to!BuildSetting(attributeName));
-
+
if (bs.lflags)
values = compiler.lflagsToDFlags( bs.lflags );
else if (bs.sourceFiles)
@@ -784,7 +785,7 @@
default: assert(0);
}
-
+
// Escape filenames and paths
if(!disableEscaping)
{
@@ -799,7 +800,7 @@
case "importPaths":
case "stringImportPaths":
return values.map!(escapeShellFileName).array();
-
+
default:
return values;
}
@@ -807,7 +808,7 @@
return values;
}
-
+
// Output a build setting without formatting for any particular compiler
private string[] formatBuildSettingPlain(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription)
{
@@ -818,21 +819,21 @@
enforce(attributeName == "targetType" || projectDescription.lookupRootPackage().targetType != TargetType.none,
"Target type is 'none'. Cannot list build settings.");
-
+
static if (attributeName == "targetType")
if (projectDescription.rootPackage !in projectDescription.targetLookup)
return ["none"];
auto targetDescription = projectDescription.lookupTarget(projectDescription.rootPackage);
auto buildSettings = targetDescription.buildSettings;
-
+
// Return any BuildSetting member attributeName as a range of strings. Don't attempt to fixup values.
// allowEmptyString: When the value is a string (as opposed to string[]),
// is empty string an actual permitted value instead of
// a missing value?
auto getRawBuildSetting(Package pack, bool allowEmptyString) {
auto value = __traits(getMember, buildSettings, attributeName);
-
+
static if( is(typeof(value) == string[]) )
return value;
else static if( is(typeof(value) == string) )
@@ -857,7 +858,7 @@
else
static assert(false, "Type of BuildSettings."~attributeName~" is unsupported.");
}
-
+
// Adjust BuildSetting member attributeName as needed.
// Returns a range of strings.
auto getFixedBuildSetting(Package pack) {
@@ -871,16 +872,16 @@
attributeName == "sourceFiles" || attributeName == "linkerFiles" ||
attributeName == "importFiles" || attributeName == "stringImportFiles" ||
attributeName == "copyFiles" || attributeName == "mainSourceFile";
-
+
// For these, empty string means "main project directory", not "missing value"
enum allowEmptyString =
attributeName == "targetPath" || attributeName == "workingDirectory";
-
+
enum isEnumBitfield =
attributeName == "requirements" || attributeName == "options";
enum isEnum = attributeName == "targetType";
-
+
auto values = getRawBuildSetting(pack, allowEmptyString);
string fixRelativePath(string importPath) { return buildNormalizedPath(pack.path.toString(), importPath); }
static string ensureTrailingSlash(string path) { return path.endsWith(dirSeparator) ? path : path ~ dirSeparator; }
@@ -972,7 +973,7 @@
enforce(false, "--data="~requestedData~
" is not a valid option. See 'dub describe --help' for accepted --data= values.");
}
-
+
assert(0);
}
@@ -994,7 +995,7 @@
auto target = projectDescription.lookupTarget(projectDescription.rootPackage);
foreach (file; target.buildSettings.sourceFiles.filter!(isLinkerFile))
target.buildSettings.addLinkerFiles(file);
-
+
// Remove linker files from sourceFiles
target.buildSettings.sourceFiles =
target.buildSettings.sourceFiles
diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d
index 3ab9c66..a5b7609 100644
--- a/source/dub/recipe/packagerecipe.d
+++ b/source/dub/recipe/packagerecipe.d
@@ -175,6 +175,8 @@
void collectFiles(string method)(in string[][string] paths_map, string pattern)
{
+ auto files = appender!(string[]);
+
foreach (suffix, paths; paths_map) {
if (!platform.matchesSpecification(suffix))
continue;
@@ -192,10 +194,12 @@
import std.path : baseName;
if (baseName(d.name)[0] == '.' || isDir(d.name)) continue;
auto src = Path(d.name).relativeTo(base_path);
- __traits(getMember, dst, method)(src.toNativeString());
+ files ~= src.toNativeString();
}
}
}
+
+ __traits(getMember, dst, method)(files.data);
}
// collect files from all source/import folders
diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d
index fc4c33f..0e2bab6 100644
--- a/source/dub/recipe/sdl.d
+++ b/source/dub/recipe/sdl.d
@@ -227,7 +227,7 @@
ret ~= m;
return ret;
}
-
+
foreach (pack, d; bs.dependencies) {
Attribute[] attribs;
if (d.path.length) attribs ~= new Attribute(null, "path", Value(d.path.toString()));
@@ -518,7 +518,7 @@
p.buildSettings.dflags["-windows"] = ["-a"];
p.buildSettings.lflags[""] = ["-b", "-c"];
auto sdl = toSDL(p).toSDLDocument();
- assert(sdl ==
+ assert(sdl ==
`name "test"
authors "foo" "bar"
dflags "-a" platform="windows"
diff --git a/source/dub/semver.d b/source/dub/semver.d
index 448ac81..ac8f6e7 100644
--- a/source/dub/semver.d
+++ b/source/dub/semver.d
@@ -20,6 +20,7 @@
import std.algorithm : max;
import std.conv;
+@safe:
/**
Validates a version string according to the SemVer specification.
@@ -229,7 +230,7 @@
auto mi = ver.indexOfAny("+-");
if (mi > 0) ver = ver[0..mi];
// Increment next to last version from a[.b[.c]].
- auto splitted = split(ver, ".");
+ auto splitted = () @trusted { return split(ver, "."); } (); // DMD 2.065.0
assert(splitted.length > 0 && splitted.length <= 3, "Version corrupt: " ~ ver);
auto to_inc = splitted.length == 3? 1 : 0;
splitted = splitted[0 .. to_inc+1];
@@ -251,7 +252,7 @@
/**
Takes a partial version and expands it to a valid SemVer version.
-
+
This function corresponds to the semantivs of the "~>" comparison operator's
lower bound.
@@ -264,7 +265,7 @@
sub = ver[mi..$];
ver = ver[0..mi];
}
- auto splitted = split(ver, ".");
+ auto splitted = () @trusted { return split(ver, "."); } (); // DMD 2.065.0
assert(splitted.length > 0 && splitted.length <= 3, "Version corrupt: " ~ ver);
while (splitted.length < 3) splitted ~= "0";
return splitted.join(".") ~ sub;
diff --git a/test/describe-dependency-1/otherdir/dummy.d b/test/describe-dependency-1/otherdir/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-1/otherdir/dummy.d
+++ b/test/describe-dependency-1/otherdir/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-dependency-1/source/dummy.d b/test/describe-dependency-1/source/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-1/source/dummy.d
+++ b/test/describe-dependency-1/source/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-dependency-2/some-extra-string-import-path/dummy.d b/test/describe-dependency-2/some-extra-string-import-path/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-2/some-extra-string-import-path/dummy.d
+++ b/test/describe-dependency-2/some-extra-string-import-path/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-dependency-2/some-path/dummy.d b/test/describe-dependency-2/some-path/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-2/some-path/dummy.d
+++ b/test/describe-dependency-2/some-path/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-dependency-3/dep3-source/dummy.d b/test/describe-dependency-3/dep3-source/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-3/dep3-source/dummy.d
+++ b/test/describe-dependency-3/dep3-source/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-dependency-3/dep3-string-import-path/dummy.d b/test/describe-dependency-3/dep3-string-import-path/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-dependency-3/dep3-string-import-path/dummy.d
+++ b/test/describe-dependency-3/dep3-string-import-path/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-project/src/dummy.d b/test/describe-project/src/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-project/src/dummy.d
+++ b/test/describe-project/src/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/describe-project/views/dummy.d b/test/describe-project/views/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/describe-project/views/dummy.d
+++ b/test/describe-project/views/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/issue1091-bogus-rebuild.sh b/test/issue1091-bogus-rebuild.sh
new file mode 100755
index 0000000..689f572
--- /dev/null
+++ b/test/issue1091-bogus-rebuild.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -e
+
+cd ${CURR_DIR}/1-exec-simple
+rm -f dub.selections.json
+${DUB} build --compiler=${DC} 2>&1 | grep -e "building configuration" -c || exit 1
+${DUB} build --compiler=${DC} 2>&1 | grep -e "building configuration" -c && exit 1 || exit 0
diff --git a/test/issue613-dynlib-pic/source/app.d b/test/issue613-dynlib-pic/source/app.d
index 26a3f4d..8b92d48 100644
--- a/test/issue613-dynlib-pic/source/app.d
+++ b/test/issue613-dynlib-pic/source/app.d
@@ -1,4 +1,4 @@
void test()
{
-
+
}
\ No newline at end of file
diff --git a/test/issue616-describe-vs-generate-commands/src/dummy.d b/test/issue616-describe-vs-generate-commands/src/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/issue616-describe-vs-generate-commands/src/dummy.d
+++ b/test/issue616-describe-vs-generate-commands/src/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/issue616-subpack/src/dummy.d b/test/issue616-subpack/src/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/issue616-subpack/src/dummy.d
+++ b/test/issue616-subpack/src/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/issue616-subsubpack/src/dummy.d b/test/issue616-subsubpack/src/dummy.d
index 8d1c8b6..8b13789 100644
--- a/test/issue616-subsubpack/src/dummy.d
+++ b/test/issue616-subsubpack/src/dummy.d
@@ -1 +1 @@
-
+
diff --git a/test/issue813-fixed-dependency/main/src/app.d b/test/issue813-fixed-dependency/main/src/app.d
index dde0c1e..0b416f0 100644
--- a/test/issue813-fixed-dependency/main/src/app.d
+++ b/test/issue813-fixed-dependency/main/src/app.d
@@ -3,4 +3,4 @@
void main()
{
foo();
-}
+}
diff --git a/test/issue813-pure-sub-dependency/main/src/app.d b/test/issue813-pure-sub-dependency/main/src/app.d
index dde0c1e..0b416f0 100644
--- a/test/issue813-pure-sub-dependency/main/src/app.d
+++ b/test/issue813-pure-sub-dependency/main/src/app.d
@@ -3,4 +3,4 @@
void main()
{
foo();
-}
+}
diff --git a/travis-ci.sh b/travis-ci.sh
index e2af598..7005767 100755
--- a/travis-ci.sh
+++ b/travis-ci.sh
@@ -21,3 +21,8 @@
dub fetch doveralls
dub run doveralls --compiler=${DC}
fi
+
+# check for trailing whitespace (needs to be done only once per build)
+if [ "$COVERAGE" = true ]; then
+ find . -type f -name '*.d' -exec grep -Hn "[[:blank:]]$" {} \;
+fi