From 1a3f8ee2942ea6e9974b6babb60e6d587a84490c Mon Sep 17 00:00:00 2001 From: Mitten Orvan Date: Sun, 1 Nov 2015 12:33:21 +0200 Subject: [PATCH] Create support for auto converting includes to imports. 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 "" --- README.markdown | 2 +- dstep/driver/Application.d | 27 ++++++++++++++ dstep/translator/IncludeHandler.d | 60 ++++++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 3a89c756..b8a561e3 100644 --- a/README.markdown +++ b/README.markdown @@ -81,7 +81,7 @@ Alternatively compile libclang yourself: ## 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: diff --git a/dstep/driver/Application.d b/dstep/driver/Application.d index 85f90dce..3e5dbb8d 100644 --- a/dstep/driver/Application.d +++ b/dstep/driver/Application.d @@ -25,6 +25,7 @@ import clang.Util; import dstep.core.Exceptions; import dstep.translator.Translator; +import dstep.translator.IncludeHandler; class Application : DStack.Application { @@ -70,6 +71,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: @@ -123,6 +132,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 () @@ -160,6 +175,16 @@ private: argsToRestore ~= language; } + void handleAutoImportPrefix (string prefix) + { + includeHandler.setAutoImportPrefix(prefix); + } + + void handleAutoImportFilter(string filter) + { + includeHandler.setAutoImportFilter(filter); + } + @property string[] remainingArgs () { return arguments.rawArgs[1 .. $] ~ argsToRestore; @@ -205,6 +230,8 @@ private: println(" -ObjC, --objective-c Treat source input file as Objective-C input."); println(" -x, --language Treat subsequent input files as having type ."); 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(); diff --git a/dstep/translator/IncludeHandler.d b/dstep/translator/IncludeHandler.d index 62be7441..49f93f71 100644 --- a/dstep/translator/IncludeHandler.d +++ b/dstep/translator/IncludeHandler.d @@ -7,6 +7,10 @@ module dstep.translator.IncludeHandler; import Path = std.path; +import std.regex; +import std.range; +import std.array; +import std.conv; import mambo.core._; @@ -26,6 +30,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 () @@ -109,21 +123,43 @@ class IncludeHandler imports ~= "core.stdc.config"; } + /// Makes includes that match regex filter be converted to import with prefix. + void setAutoImportPrefix (string prefix) + { + this.convertIncludes = true; + this.importPrefix = prefix; + } + + /// Makes includes that match regex filter be converted to import with prefix. + void setAutoImportFilter (string filter) + { + this.convertIncludes = true; + this.convertableIncludePattern = regex(filter); + } + string[] toImports () { - auto r = rawIncludes.map!((e) { + auto r = mambo.core.Array.map!((e) { if (auto i = isKnownInclude(e)) return toImport(i); - + else if( this.convertIncludes && isConvertableInclude(e) ) + return toImport(autoConvertInclude(e)); else return ""; - }); + })(rawIncludes); - auto imps = imports.map!(e => toImport(e)); + auto imps = mambo.core.Array.map!(e => toImport(e))(imports); return r.append(imps).filter!(e => e.any).unique.toArray; } + /// Returns the base name (last component without extension) of a file path. + static string baseName (string path) + { + string last_component = text(retro(Path.pathSplitter(path)).front); + return Path.stripExtension( last_component ); + } + private: string toImport (string str) @@ -140,4 +176,18 @@ private: return null; } -} \ No newline at end of file + + /// 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 != "" && cast(bool)(matchFirst(include, convertableIncludePattern)); + } + + + /// Generates an importable module name from an include file name. + string autoConvertInclude(string include) + { + return this.importPrefix ~ baseName(include); + } +}