1 module reggae.rules.common; 2 3 4 import reggae.build; 5 import reggae.config: projectPath; 6 import std.algorithm; 7 import std.path; 8 import std.array: array; 9 10 version(Windows) { 11 immutable objExt = ".obj"; 12 immutable exeExt = ".exe"; 13 } else { 14 immutable objExt = ".o"; 15 immutable exeExt = ""; 16 } 17 18 19 package string objFileName(in string srcFileName) @safe pure nothrow { 20 import std.path: stripExtension, defaultExtension, isRooted, stripDrive; 21 immutable localFileName = srcFileName.isRooted 22 ? srcFileName.stripDrive[1..$] 23 : srcFileName; 24 return localFileName.stripExtension.defaultExtension(objExt); 25 } 26 27 28 Target[] srcObjects(alias func)(in string extension, 29 string[] dirs, 30 string[] srcFiles, 31 in string[] excludeFiles) { 32 auto files = selectSrcFiles(srcFilesInDirs(extension, dirs), srcFiles, excludeFiles); 33 return func(files); 34 } 35 36 //The parameters would be "in" except that "remove" doesn't like that... 37 string[] selectSrcFiles(string[] dirFiles, 38 string[] srcFiles, 39 in string[] excludeFiles) @safe pure nothrow { 40 return (dirFiles ~ srcFiles).remove!(a => excludeFiles.canFind(a)).array; 41 } 42 43 private string[] srcFilesInDirs(in string extension, in string[] dirs) { 44 import std.exception: enforce; 45 import std.file; 46 import std.path: buildNormalizedPath, buildPath; 47 import std.array: array; 48 49 DirEntry[] modules; 50 foreach(dir; dirs.map!(a => buildPath(projectPath, a))) { 51 enforce(isDir(dir), dir ~ " is not a directory name"); 52 auto entries = dirEntries(dir, "*." ~ extension, SpanMode.depth); 53 auto normalised = entries.map!(a => DirEntry(buildNormalizedPath(a))); 54 modules ~= array(normalised); 55 } 56 57 return modules.map!(a => a.name.removeProjectPath).array; 58 } 59 60 string removeProjectPath(in string path) @safe pure { 61 import std.path: relativePath, absolutePath; 62 return path.absolutePath.relativePath(projectPath.absolutePath); 63 } 64 65 @safe: 66 /** 67 An object file, typically from one source file in a certain language 68 (although for D the default is a whole package. The language is determined 69 by the file extension of the file(s) passed in. 70 */ 71 Target objectFile(in string srcFileName, 72 in string flags = "", 73 in string[] includePaths = [], 74 in string[] stringImportPaths = [], 75 in string projDir = "$project") pure { 76 77 immutable cmd = compileCommand(srcFileName, flags, includePaths, stringImportPaths, projDir); 78 return Target(srcFileName.objFileName, cmd, [Target(srcFileName)]); 79 } 80 81 82 string compileCommand(in string srcFileName, 83 in string flags = "", 84 in string[] includePaths = [], 85 in string[] stringImportPaths = [], 86 in string projDir = "$project") pure { 87 immutable includeParams = includePaths.map!(a => "-I" ~ buildPath(projDir, a)).join(","); 88 immutable flagParams = flags.splitter.join(","); 89 immutable ruleName = getBuiltinRule(srcFileName); 90 auto cmd = [ruleName, "includes=" ~ includeParams, "flags=" ~ flagParams]; 91 if(ruleName == "_dcompile") 92 cmd ~= "stringImports=" ~ stringImportPaths.map!(a => "-J" ~ buildPath(projDir, a)).join(","); 93 94 return cmd.join(" "); 95 } 96 97 enum Language { 98 C, 99 Cplusplus, 100 D, 101 } 102 103 private Language getLanguage(in string srcFileName) pure { 104 switch(srcFileName.extension) with(Language) { 105 case ".d": 106 return D; 107 case ".cpp": 108 case ".CPP": 109 case ".C": 110 case ".cxx": 111 case ".c++": 112 case ".cc": 113 return Cplusplus; 114 case ".c": 115 return C; 116 default: 117 throw new Exception("Unknown file extension " ~ srcFileName.extension); 118 } 119 120 } 121 122 private string getBuiltinRule(in string srcFileName) pure { 123 final switch(getLanguage(srcFileName)) with(Language) { 124 case D: 125 return "_dcompile"; 126 case Cplusplus: 127 return "_cppcompile"; 128 case C: 129 return "_ccompile"; 130 } 131 } 132 133 134 /** 135 Should pull its weight more in the future by automatically figuring out what 136 to do. Right now only works for linking D applications using the configured 137 D compiler 138 */ 139 Target link(in string exeName, in Target[] dependencies, in string flags = "") @safe pure nothrow { 140 auto cmd = "_link"; 141 if(flags != "") cmd ~= " flags=" ~ flags; 142 return Target(exeName, cmd, dependencies); 143 }