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 }