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 = Path 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(Path(filename), parent_name);
28 }
29 /// ditto
30 PackageRecipe readPackageRecipe(Path 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 
57 	Returns: Returns the package recipe contents
58 	Throws: Throws an exception if an I/O or syntax error occurs
59 */
60 PackageRecipe parsePackageRecipe(string contents, string filename, string parent_name = null)
61 {
62 	import std.algorithm : endsWith;
63 	import dub.internal.vibecompat.data.json;
64 	import dub.recipe.json : parseJson;
65 	import dub.recipe.sdl : parseSDL;
66 
67 	PackageRecipe ret;
68 
69 	if (filename.endsWith(".json")) parseJson(ret, parseJsonString(contents, filename), parent_name);
70 	else if (filename.endsWith(".sdl")) parseSDL(ret, contents, parent_name, filename);
71 	else assert(false, "readPackageRecipe called with filename with unknown extension: "~filename);
72 	return ret;
73 }
74 
75 
76 unittest { // issue #711 - configuration default target type not correct for SDL
77 	import dub.compilers.buildsettings : TargetType;
78 	auto inputs = [
79 		"dub.sdl": "name \"test\"\nconfiguration \"a\" {\n}",
80 		"dub.json": "{\"name\": \"test\", \"configurations\": [{\"name\": \"a\"}]}"
81 	];
82 	foreach (file, content; inputs) {
83 		auto pr = parsePackageRecipe(content, file);
84 		assert(pr.name == "test");
85 		assert(pr.configurations.length == 1);
86 		assert(pr.configurations[0].name == "a");
87 		assert(pr.configurations[0].buildSettings.targetType == TargetType.library);
88 	}
89 }
90 
91 unittest { // issue #711 - configuration default target type not correct for SDL
92 	import dub.compilers.buildsettings : TargetType;
93 	auto inputs = [
94 		"dub.sdl": "name \"test\"\ntargetType \"autodetect\"\nconfiguration \"a\" {\n}",
95 		"dub.json": "{\"name\": \"test\", \"targetType\": \"autodetect\", \"configurations\": [{\"name\": \"a\"}]}"
96 	];
97 	foreach (file, content; inputs) {
98 		auto pr = parsePackageRecipe(content, file);
99 		assert(pr.name == "test");
100 		assert(pr.configurations.length == 1);
101 		assert(pr.configurations[0].name == "a");
102 		assert(pr.configurations[0].buildSettings.targetType == TargetType.library);
103 	}
104 }
105 
106 unittest { // issue #711 - configuration default target type not correct for SDL
107 	import dub.compilers.buildsettings : TargetType;
108 	auto inputs = [
109 		"dub.sdl": "name \"test\"\ntargetType \"executable\"\nconfiguration \"a\" {\n}",
110 		"dub.json": "{\"name\": \"test\", \"targetType\": \"executable\", \"configurations\": [{\"name\": \"a\"}]}"
111 	];
112 	foreach (file, content; inputs) {
113 		auto pr = parsePackageRecipe(content, file);
114 		assert(pr.name == "test");
115 		assert(pr.configurations.length == 1);
116 		assert(pr.configurations[0].name == "a");
117 		assert(pr.configurations[0].buildSettings.targetType == TargetType.executable);
118 	}
119 }
120 
121 
122 /** Writes the textual representation of a package recipe to a file.
123 
124 	Note that the file extension must be either "json" or "sdl".
125 */
126 void writePackageRecipe(string filename, in ref PackageRecipe recipe)
127 {
128 	import dub.internal.vibecompat.core.file : openFile, FileMode;
129 	auto f = openFile(filename, FileMode.createTrunc);
130 	scope(exit) f.close();
131 	serializePackageRecipe(f, recipe, filename);
132 }
133 
134 /// ditto
135 void writePackageRecipe(Path filename, in ref PackageRecipe recipe)
136 {
137 	writePackageRecipe(filename.toNativeString, recipe);
138 }
139 
140 /** Converts a package recipe to its textual representation.
141 
142 	The extension of the supplied `filename` must be either "json" or "sdl".
143 	The output format is chosen accordingly.
144 */
145 void serializePackageRecipe(R)(ref R dst, in ref PackageRecipe recipe, string filename)
146 {
147 	import std.algorithm : endsWith;
148 	import dub.internal.vibecompat.data.json : writeJsonString;
149 	import dub.recipe.json : toJson;
150 	import dub.recipe.sdl : toSDL;
151 
152 	if (filename.endsWith(".json"))
153 		dst.writeJsonString!(R, true)(toJson(recipe));
154 	else if (filename.endsWith(".sdl"))
155 		toSDL(recipe).toSDLDocument(dst);
156 	else assert(false, "writePackageRecipe called with filename with unknown extension: "~filename);
157 }
158