Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dstep/Configuration.d
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ struct Configuration
/// output file name or folder (in case there are many input files)
string output;

/// when true then output to a directory
bool useDirOutput = false;

/// package name
@("package", "Use <package> as package name.")
string packageName;
Expand Down
68 changes: 50 additions & 18 deletions dstep/driver/Application.d
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dstep.translator.Options;
import dstep.core.Exceptions;
import dstep.translator.Options;
import dstep.translator.Translator;
import dstep.driver.Util : findBasePath, findFiles;

class Application
{
Expand All @@ -41,11 +42,11 @@ class Application
import std.exception : enforce;
import std.range;

this.config = prepareConfig(config);

enforce!DStepException(config.inputFiles.length > 0,
"dstep: error: must supply at least one input file\n");

enforceInputFilesExist(config);

auto translationUnits = makeTranslationUnits(config);

enforceTranslationUnitsCompiled(translationUnits);
Expand All @@ -67,21 +68,45 @@ class Application
}
}

static void enforceInputFilesExist(const Configuration config)
static Configuration prepareConfig(Configuration config)
{
import std.file : exists, isDir, isFile;
import std.path : baseName, asAbsolutePath;
import std.exception : enforce;
import std.format : format;
import std.array : array;

Configuration result = config;
string[] files;

// when only one input file is supplied, -o argument is
// interpreted as file path, otherwise as base directory path
result.useDirOutput = config.inputFiles.length != 1;

foreach (inputFile; config.inputFiles)
foreach (input; config.inputFiles)
{
enforce!DStepException(
exists(inputFile),
format("dstep: error: file '%s' doesn't exist\n", inputFile));
exists(input),
format("dstep: error: input '%s' doesn't exist\n", input));

enforce!DStepException(
isFile(inputFile),
format("dstep: error: '%s' is not a file\n", inputFile));
if (isDir(input))
{
files ~= findFiles(input);
result.useDirOutput = true;
}
else
{
enforce!DStepException(
isFile(input),
format("dstep: error: '%s' is not a file\n", input));

files ~= input.asAbsolutePath.array;
}
}

result.inputFiles = files;

return result;
}

void enforceTranslationUnitsCompiled(TranslationUnit[] translationUnits)
Expand Down Expand Up @@ -115,26 +140,33 @@ class Application
import std.algorithm;
import std.array;
import std.range;
import std.path;

import dstep.driver.Util : makeDefaultOutputFile;
import dstep.driver.Util : makeDefaultOutputFile, findBasePath;

auto inputFiles = config.inputFiles;

// when only one input file is supplied, -o argument is
// interpreted as file path, otherwise as base directory path
if (inputFiles.length == 1)
if (!config.useDirOutput)
{
return [config.output.empty
? makeDefaultOutputFile(inputFiles.front, false)
: config.output];
}
else
{
alias fmap = file => Path.buildPath(
config.output,
makeDefaultOutputFile(file, false));

return inputFiles.map!fmap.array;
if (config.output.empty)
{
alias fmap = file => makeDefaultOutputFile(file, false);
return inputFiles.map!fmap.array;
}
else
{
auto basePath = findBasePath(inputFiles);
alias fmap = file => Path.buildPath(
config.output,
makeDefaultOutputFile(relativePath(file, basePath), false));
return inputFiles.map!fmap.array;
}
}
}

Expand Down
105 changes: 105 additions & 0 deletions dstep/driver/Util.d
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,108 @@ string makeDefaultOutputFile(string inputFile, bool useBaseName = true)

return setExtension(inputFile, "d");
}

string findBasePath(string[] paths)
{
import std.algorithm.iteration : filter, map, reduce;
import std.algorithm.searching : commonPrefix;
import std.array : array;
import std.path : isAbsolute, dirName, pathSplitter, buildPath;
import std.file : exists, isDir;

auto absolutePaths = paths.filter!isAbsolute.array;
if (absolutePaths.length == 0)
return ".";

version (Windows)
{
import std.string : replace;
absolutePaths = absolutePaths.map!(p => p.replace("/", "\\")).array;
}

auto getDirName = (string path) => (exists(path) && isDir(path)) ? path : dirName(path);

if (absolutePaths.length == 1)
return getDirName(absolutePaths[0]);

auto parts = absolutePaths
.map!(p => pathSplitter(getDirName(p)).array)
.array;

version (Windows)
{
import std.uni : icmp;
auto common = parts.reduce!((a, b) => commonPrefix!((x, y) => icmp(x, y) == 0)(a, b).array);
}
else
{
auto common = parts.reduce!((a, b) => commonPrefix(a, b).array);
}

if (common.length == 0)
return ".";

return buildPath(common);
}

string[] findFiles(string dir, string[] extensions = [".h", ".hpp"])
{
import std.file : dirEntries, SpanMode;
import std.path : extension, asAbsolutePath;
import std.algorithm : filter, map, canFind;
import std.array : array;
import std.conv : to;

return dirEntries(dir, SpanMode.breadth)
.filter!(f => f.isFile && extensions.canFind(extension(f.name)))
.map!(f => f.name.asAbsolutePath.to!string)
.array;
}

unittest
{
assert(findBasePath([]) == ".");
assert(findBasePath(["foo/bar.h", "a/b.h", "c.h"]) == ".");
}

version (Posix)
{
unittest
{
assert(findBasePath(["/usr/include/stdio.h"]) == "/usr/include");
assert(findBasePath(["foo.h", "/usr/include/sys/stat.h"]) == "/usr/include/sys");
assert(findBasePath(["/usr/include/stdio.h", "/usr/include/sys/stat.h", "/usr/lib/foo.h"]) == "/usr");
assert(findBasePath(["/usr/include/stdio.h", "/usr/include/string.h", "/usr/include/stdlib.h"]) == "/usr/include");
assert(findBasePath(["usr.h", "/usr/include/stdio.h", "/usr/include/sys/stat.h", "/usr/include/string.h", "include.h"]) == "/usr/include");
assert(findBasePath(["/usr/include/stdio.h", "/usr/Include/stdio.h", "/usR/include/stdio.h"]) == "/");
assert(findBasePath(["/usr"]) == "/usr");
assert(findBasePath(["/usr", "/usr/lib"]) == "/usr");
}
}

version (Windows)
{
unittest
{
assert(findBasePath(["/usr/include/stdio.h"]) == ".");
assert(findBasePath(["foo.h", "/usr/include/sys/stat.h"]) == ".");
assert(findBasePath(["/usr/include/stdio.h", "/usr/include/sys/stat.h", "/usr/lib/foo.h"]) == ".");
assert(findBasePath(["/usr/include/stdio.h", "/usr/include/string.h", "/usr/include/stdlib.h"]) == ".");
assert(findBasePath(["usr.h", "/usr/include/stdio.h", "/usr/include/sys/stat.h", "/usr/include/string.h", "include.h"]) == ".");
assert(findBasePath(["/usr/include/stdio.h", "/usr/Include/stdio.h", "/usR/include/stdio.h"]) == ".");

assert(findBasePath(["C:/usr/include/stdio.h"]) == "C:\\usr\\include");
assert(findBasePath(["foo.h", "C:/usr/include/sys/stat.h"]) == "C:\\usr\\include\\sys");
assert(findBasePath(["C:/usr/include/stdio.h", "C:/usr/include/sys/stat.h", "C:/usr/lib/foo.h"]) == "C:\\usr");
assert(findBasePath(["C:/usr/include/stdio.h", "C:/usr/include/string.h", "C:/usr/include/stdlib.h"]) == "C:\\usr\\include");
assert(findBasePath(["usr.h", "D:/usr/include/stdio.h", "D:/usr/include/sys/stat.h", "D:/usr/include/string.h", "include.h"]) == "D:\\usr\\include");
assert(findBasePath(["E:/usr/include/stdio.h", "E:/usr/Include/stdio.h", "E:/usR/include/stdio.h"]) == "E:\\usr\\include");

assert(findBasePath(["D:\\test.h"]) == "D:\\");
assert(findBasePath(["D:\\a\\b.h", "D:\\a\\c.h"]) == "D:\\a");
assert(findBasePath(["D:\\a\\b.h", "E:\\b.h"]) == ".");
assert(findBasePath(["D:\\B\\a.h", "D:\\b\\c\\D.h", "D:\\B\\c.h"]) == "D:\\B");
assert(findBasePath(["D:/a\\b/d.h", "D:\\a/B\\c.h"]) == "D:\\a\\b");
assert(findBasePath(["C:\\a.h", "D:\\b.h"]) == ".");
}
}
62 changes: 62 additions & 0 deletions tests/functional/Tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import tests.support.Assertions;
import std.algorithm : canFind;
import std.process : executeShell;
import std.typecons;
import std.path : absolutePath;

void printClangVersion()
{
Expand Down Expand Up @@ -205,6 +206,29 @@ unittest
);
}

unittest
{
assertRunsDStepCDir(["tests/functional/module/include.d",
"tests/functional/module/main0.d",
"IGNORE",
"IGNORE",
"tests/functional/module/unused.d"],
"tests/functional/module",
["--package", "modules", "-Iresources"]
);
}

unittest
{
assertRunsDStepCDir(["tests/functional/module/include.d",
"IGNORE",
"tests/functional/module/main0NotNormalized.d",
"tests/functional/module/main0_public.d"],
"tests/functional/module",
["--public-submodules", "--package", "modules", "-Iresources", "--unspecified-output"]
);
}

unittest
{
assertRunsDStepCFiles([
Expand Down Expand Up @@ -294,6 +318,44 @@ unittest
assertIssuesWarning("tests/functional/collision.h");
}

// DStep should output to correct directory when using absolute paths.
unittest
{
assertRunsDStepCFiles([
TestFile("tests/functional/graph/subfile1.d", absolutePath("tests/functional/graph/subfile1.h")),
TestFile("tests/functional/include/subfile3.d", absolutePath("tests/functional/include/subfile3.h"))]
);
}

unittest
{
assertRunsDStepCFile(
"tests/functional/functions.d",
absolutePath("tests/functional/functions.h")
);
}

// Output in same directory when single file given and no output specified
unittest
{
assertRunsDStepCFiles(
[TestFile("tests/functional/functions.d", absolutePath("tests/functional/functions.h"))],
["--unspecified-output"],
false);
}

// Output in same directory when using multiple files with no output folder specified
unittest
{
assertRunsDStepCFiles(
[TestFile("tests/functional/aggregate.d", "tests/functional/aggregate.h"),
TestFile("tests/functional/functions.d", "tests/functional/functions.h"),
TestFile("tests/functional/graph/subfile1.d", absolutePath("tests/functional/graph/subfile1.h")),
TestFile("tests/functional/include/subfile3.d", absolutePath("tests/functional/include/subfile3.h"))],
["--unspecified-output"]
);
}

version (OSX):

// Objective-C tests
Expand Down
3 changes: 3 additions & 0 deletions tests/functional/graph/subfile1.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extern (C):

extern __gshared int content;
3 changes: 3 additions & 0 deletions tests/functional/include/subfile3.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extern (C):

void core (int);
Loading
Loading