Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create support for auto converting includes to imports. #43

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
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));
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phobos already contains baseName [1], would that work?

[1] http://dlang.org/phobos/std_path.html#.baseName


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;