Skip to content

Commit

Permalink
Create support for auto converting includes to imports.
Browse files Browse the repository at this point in the history
Added command line options
--import-filter
A regular expression. All includes matching this will be converted to imports.
Only the basename part of the file name is used to generate the import.
--import-prefix
A prefix to give the imports. The import will have the form "<prefix>.<basename>"

Includes a simple test case.
  • Loading branch information
Mitten-O committed Jan 18, 2016
1 parent f2008db commit 32619d0
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Use `-h` for usage information. Any flags recognized by Clang can be used.
## Limitations/Known issues

* Doesn't translate preprocessor macros of any kind
* Doesn't translate `#include` to `import`. A few standard C headers are translated
* Only very simplistic translation of `#include` to `import`. A few standard C headers are translated
* Doesn't translate C++ at all
* Umbrella headers. Some headers just serve to include other headers. If these other headers contain some form of protection, like `#error`, to be included directly this can cause problems for DStep
* Some headers are designed to always be included together with other header files. These headers may very well use symbols from other header files without including them itself. Since DStep is designed to convert header files one-by-one this doesn't work. There are two workarounds for this:
Expand Down
27 changes: 27 additions & 0 deletions dstep/driver/Application.d
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import clang.Util;

import dstep.core.Exceptions;
import dstep.translator.Translator;
import dstep.translator.IncludeHandler;

class Application : DStack.Application
{
Expand Down Expand Up @@ -77,6 +78,14 @@ class Application : DStack.Application
.on(&handleLanguage);

arguments("objective-c", "Treat source input file as Objective-C input.");

arguments('f',"import-filter", "A regex to filter includes that will be auto converted.")
.params(1)
.defaults(".*");

arguments('p',"import-prefix", "A prefix to add to any custom generated import")
.params(1)
.defaults("");
}

private:
Expand Down Expand Up @@ -131,6 +140,12 @@ private:
// FIXME: Cannot use type inference here, probably a bug. Results in segfault.
if (arguments.rawArgs.any!((string e) => e == "-ObjC"))
handleObjectiveC();

if (arguments["import-prefix"].hasValue)
handleAutoImportPrefix(arguments["import-prefix"].value);

if (arguments["import-filter"].hasValue)
handleAutoImportFilter(arguments["import-filter"].value);
}

void handleObjectiveC ()
Expand Down Expand Up @@ -168,6 +183,16 @@ private:
argsToRestore ~= language;
}

void handleAutoImportPrefix (string prefix)
{
includeHandler.autoImportPrefix = prefix;
}

void handleAutoImportFilter (string filter)
{
includeHandler.autoImportFilter = filter;
}

@property string[] remainingArgs ()
{
return arguments.rawArgs[1 .. $] ~ argsToRestore;
Expand Down Expand Up @@ -213,6 +238,8 @@ private:
println(" -ObjC, --objective-c Treat source input file as Objective-C input.");
println(" -x, --language <language> Treat subsequent input files as having type <language>.");
println(" -h, --help Show this message and exit.");
println(" -f, --import-filter A regex to filter includes that will be auto converted to imports.");
println(" -p, --import-prefix A prefix to add to any import generated from an include.");
println();
println("All options that Clang accepts can be used as well.");
println();
Expand Down
69 changes: 65 additions & 4 deletions dstep/translator/IncludeHandler.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
module dstep.translator.IncludeHandler;

import Path = std.path;
import std.regex;
import std.conv;

import mambo.core._;
import mambo.core.Array;

private IncludeHandler includeHandler_;

Expand All @@ -26,6 +28,16 @@ class IncludeHandler
{
private string[] rawIncludes;
private string[] imports;

// True if includes should be converted to imports.
private bool convertIncludes = false;

// Includes matching this will be converted to imports.
private Regex!char convertableIncludePattern = regex(".*");

// Prefix for auto generated imports.
private string importPrefix = "";

static string[string] knownIncludes;

static this ()
Expand Down Expand Up @@ -109,12 +121,33 @@ class IncludeHandler
imports ~= "core.stdc.config";
}


/// Makes includes that match regex filter be converted to import with prefix.
@property void autoImportPrefix (string prefix)
{
convertIncludes = true;
importPrefix = prefix;
}

@property string autoImportPrefix ()
{
return this.importPrefix;
}

/// Makes includes that match regex filter be converted to import with prefix.
@property void autoImportFilter (string filter)
{
convertIncludes = true;
convertableIncludePattern = regex(filter);
}

string[] toImports ()
{
auto r = rawIncludes.map!((e) {
auto r = rawIncludes.map!((e) {
if (auto i = isKnownInclude(e))
return toImport(i);

else if (convertIncludes && isConvertableInclude(e) )
return toImport(autoConvertInclude(e));
else
return "";
});
Expand All @@ -124,6 +157,12 @@ class IncludeHandler
return r.append(imps).filter!(e => e.any).unique.toArray;
}

/// Returns the base name (last component without extension) of a file path.
static string bareName (string path)
{
return Path.stripExtension(Path.baseName(path));
}

private:

string toImport (string str)
Expand All @@ -140,4 +179,26 @@ private:

return null;
}
}

/// Checks if the given include file name should be converted to an import declaration.
bool isConvertableInclude (string include)
{
// Do not try to convert empty strings, no matter what the pattern says.
return include != "" && matchFirst(include, convertableIncludePattern);
}


/// Generates an importable module name from an include file name.
string autoConvertInclude (string include)
{
string prefix = importPrefix;

// If we have a prefix, append a dot to it if it doesn't already have one at the end.
if (prefix != "" && !prefix.endsWith("."))
{
prefix ~= ".";
}

return prefix ~ bareName(include);
}
}
5 changes: 5 additions & 0 deletions features/include_conversion.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Feature: Include Conversion

Scenario: Convert includes
Then I test the file "includes" with filter "dummy_includes/transform.*" and prefix "transformed"

7 changes: 7 additions & 0 deletions features/step_definitions/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
step %{the files "#{file}.d" and "test_files/#{path}/#{file}.d" should be equal}
end

Then /^I test the file "([^"]*)" with filter "([^"]*)" and prefix "([^"]*)"$/ do |file, filter, prefix|
step %{a test file named "#{file}"}
step %{an expected file named "#{file}"}
step %{I successfully convert the test file "#{file}" in "" with the flags "--import-filter #{filter} --import-prefix #{prefix}"}
step %{the files "#{file}.d" and "test_files/#{file}.d" should be equal}
end

if OSX
Then /^I test the Objective\-C file "([^"]*)" in "([^"]*)"$/ do |file, path|
step %{a test file named "#{file}" in "#{path}"}
Expand Down
8 changes: 8 additions & 0 deletions test_files/dummy_includes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Directory dummy_includes

The support for import generation works based on the use of types
haling from specific includes. That is why to test it
we need a few dummy includes that define types.

(Other test files cannot be used because they need
to be free to contain name conflicts.)
1 change: 1 addition & 0 deletions test_files/dummy_includes/ignored.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
struct Ignored{};
1 change: 1 addition & 0 deletions test_files/dummy_includes/transform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
struct Transform{};
1 change: 1 addition & 0 deletions test_files/dummy_includes/transform_also.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
struct TransformAlso{};
5 changes: 5 additions & 0 deletions test_files/includes.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import transformed.transform;
import transformed.transform_also;

extern (C):

10 changes: 10 additions & 0 deletions test_files/includes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "dummy_includes/transform.h"
#include "dummy_includes/ignored.h"
#include "dummy_includes/transform_also.h"

// Dstep only pays attention to includes whose types are used so we
// use a type from each here.
struct Transform transform;
struct Ignored ignored;
struct TransformAlso transform_also;

0 comments on commit 32619d0

Please sign in to comment.