1 /** 2 Package recipe reading/writing facilities. 3 4 Copyright: © 2015-2016, Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module dub.recipe.io; 9 10 import dub.recipe.packagerecipe; 11 import dub.internal.vibecompat.inet.path; 12 13 14 /** Reads a package recipe from a file. 15 16 The file format (JSON/SDLang) will be determined from the file extension. 17 18 Params: 19 filename = NativePath of the package recipe file 20 parent_name = Optional name of the parent package (if this is a sub package) 21 22 Returns: Returns the package recipe contents 23 Throws: Throws an exception if an I/O or syntax error occurs 24 */ 25 PackageRecipe readPackageRecipe(string filename, string parent_name = null) 26 { 27 return readPackageRecipe(NativePath(filename), parent_name); 28 } 29 /// ditto 30 PackageRecipe readPackageRecipe(NativePath filename, string parent_name = null) 31 { 32 import dub.internal.utils : stripUTF8Bom; 33 import dub.internal.vibecompat.core.file : openFile, FileMode; 34 35 string text; 36 37 { 38 auto f = openFile(filename.toNativeString(), FileMode.read); 39 scope(exit) f.close(); 40 text = stripUTF8Bom(cast(string)f.readAll()); 41 } 42 43 return parsePackageRecipe(text, filename.toNativeString(), parent_name); 44 } 45 46 /** Parses an in-memory package recipe. 47 48 The file format (JSON/SDLang) will be determined from the file extension. 49 50 Params: 51 contents = The contents of the recipe file 52 filename = Name associated with the package recipe - this is only used 53 to determine the file format from the file extension 54 parent_name = Optional name of the parent package (if this is a sub 55 package) 56 default_package_name = Optional default package name (if no package name 57 is found in the recipe this value will be used) 58 59 Returns: Returns the package recipe contents 60 Throws: Throws an exception if an I/O or syntax error occurs 61 */ 62 PackageRecipe parsePackageRecipe(string contents, string filename, string parent_name = null, 63 string default_package_name = null) 64 { 65 import std.algorithm : endsWith; 66 import dub.internal.vibecompat.data.json; 67 import dub.recipe.json : parseJson; 68 import dub.recipe.sdl : parseSDL; 69 70 PackageRecipe ret; 71 72 ret.name = default_package_name; 73 74 if (filename.endsWith(".json")) parseJson(ret, parseJsonString(contents, filename), parent_name); 75 else if (filename.endsWith(".sdl")) parseSDL(ret, contents, parent_name, filename); 76 else assert(false, "readPackageRecipe called with filename with unknown extension: "~filename); 77 return ret; 78 } 79 80 81 unittest { // issue #711 - configuration default target type not correct for SDL 82 import dub.compilers.buildsettings : TargetType; 83 auto inputs = [ 84 "dub.sdl": "name \"test\"\nconfiguration \"a\" {\n}", 85 "dub.json": "{\"name\": \"test\", \"configurations\": [{\"name\": \"a\"}]}" 86 ]; 87 foreach (file, content; inputs) { 88 auto pr = parsePackageRecipe(content, file); 89 assert(pr.name == "test"); 90 assert(pr.configurations.length == 1); 91 assert(pr.configurations[0].name == "a"); 92 assert(pr.configurations[0].buildSettings.targetType == TargetType.library); 93 } 94 } 95 96 unittest { // issue #711 - configuration default target type not correct for SDL 97 import dub.compilers.buildsettings : TargetType; 98 auto inputs = [ 99 "dub.sdl": "name \"test\"\ntargetType \"autodetect\"\nconfiguration \"a\" {\n}", 100 "dub.json": "{\"name\": \"test\", \"targetType\": \"autodetect\", \"configurations\": [{\"name\": \"a\"}]}" 101 ]; 102 foreach (file, content; inputs) { 103 auto pr = parsePackageRecipe(content, file); 104 assert(pr.name == "test"); 105 assert(pr.configurations.length == 1); 106 assert(pr.configurations[0].name == "a"); 107 assert(pr.configurations[0].buildSettings.targetType == TargetType.library); 108 } 109 } 110 111 unittest { // issue #711 - configuration default target type not correct for SDL 112 import dub.compilers.buildsettings : TargetType; 113 auto inputs = [ 114 "dub.sdl": "name \"test\"\ntargetType \"executable\"\nconfiguration \"a\" {\n}", 115 "dub.json": "{\"name\": \"test\", \"targetType\": \"executable\", \"configurations\": [{\"name\": \"a\"}]}" 116 ]; 117 foreach (file, content; inputs) { 118 auto pr = parsePackageRecipe(content, file); 119 assert(pr.name == "test"); 120 assert(pr.configurations.length == 1); 121 assert(pr.configurations[0].name == "a"); 122 assert(pr.configurations[0].buildSettings.targetType == TargetType.executable); 123 } 124 } 125 126 127 /** Writes the textual representation of a package recipe to a file. 128 129 Note that the file extension must be either "json" or "sdl". 130 */ 131 void writePackageRecipe(string filename, const scope ref PackageRecipe recipe) 132 { 133 import dub.internal.vibecompat.core.file : openFile, FileMode; 134 auto f = openFile(filename, FileMode.createTrunc); 135 scope(exit) f.close(); 136 serializePackageRecipe(f, recipe, filename); 137 } 138 139 /// ditto 140 void writePackageRecipe(NativePath filename, const scope ref PackageRecipe recipe) 141 { 142 writePackageRecipe(filename.toNativeString, recipe); 143 } 144 145 /** Converts a package recipe to its textual representation. 146 147 The extension of the supplied `filename` must be either "json" or "sdl". 148 The output format is chosen accordingly. 149 */ 150 void serializePackageRecipe(R)(ref R dst, const scope ref PackageRecipe recipe, string filename) 151 { 152 import std.algorithm : endsWith; 153 import dub.internal.vibecompat.data.json : writeJsonString; 154 import dub.recipe.json : toJson; 155 import dub.recipe.sdl : toSDL; 156 157 if (filename.endsWith(".json")) 158 dst.writeJsonString!(R, true)(toJson(recipe)); 159 else if (filename.endsWith(".sdl")) 160 toSDL(recipe).toSDLDocument(dst); 161 else assert(false, "writePackageRecipe called with filename with unknown extension: "~filename); 162 }