8
8
module dub.recipe.io ;
9
9
10
10
import dub.recipe.packagerecipe;
11
+ import dub.internal.logging;
11
12
import dub.internal.vibecompat.inet.path;
12
-
13
+ import configy.Read;
13
14
14
15
/* * Reads a package recipe from a file.
15
16
@@ -18,16 +19,20 @@ import dub.internal.vibecompat.inet.path;
18
19
Params:
19
20
filename = NativePath of the package recipe file
20
21
parent_name = Optional name of the parent package (if this is a sub package)
22
+ mode = Whether to issue errors, warning, or ignore unknown keys in dub.json
21
23
22
24
Returns: Returns the package recipe contents
23
25
Throws: Throws an exception if an I/O or syntax error occurs
24
26
*/
25
- PackageRecipe readPackageRecipe (string filename, string parent_name = null )
27
+ PackageRecipe readPackageRecipe (
28
+ string filename, string parent_name = null , StrictMode mode = StrictMode.Ignore)
26
29
{
27
- return readPackageRecipe (NativePath(filename), parent_name);
30
+ return readPackageRecipe (NativePath(filename), parent_name, mode );
28
31
}
32
+
29
33
// / ditto
30
- PackageRecipe readPackageRecipe (NativePath filename, string parent_name = null )
34
+ PackageRecipe readPackageRecipe (
35
+ NativePath filename, string parent_name = null , StrictMode mode = StrictMode.Ignore)
31
36
{
32
37
import dub.internal.utils : stripUTF8Bom;
33
38
import dub.internal.vibecompat.core.file : openFile, FileMode;
@@ -40,7 +45,7 @@ PackageRecipe readPackageRecipe(NativePath filename, string parent_name = null)
40
45
text = stripUTF8Bom(cast (string )f.readAll());
41
46
}
42
47
43
- return parsePackageRecipe (text, filename.toNativeString(), parent_name);
48
+ return parsePackageRecipe (text, filename.toNativeString(), parent_name, null , mode );
44
49
}
45
50
46
51
/* * Parses an in-memory package recipe.
@@ -55,12 +60,13 @@ PackageRecipe readPackageRecipe(NativePath filename, string parent_name = null)
55
60
package)
56
61
default_package_name = Optional default package name (if no package name
57
62
is found in the recipe this value will be used)
63
+ mode = Whether to issue errors, warning, or ignore unknown keys in dub.json
58
64
59
65
Returns: Returns the package recipe contents
60
66
Throws: Throws an exception if an I/O or syntax error occurs
61
67
*/
62
68
PackageRecipe parsePackageRecipe (string contents, string filename, string parent_name = null ,
63
- string default_package_name = null )
69
+ string default_package_name = null , StrictMode mode = StrictMode.Ignore )
64
70
{
65
71
import std.algorithm : endsWith;
66
72
import dub.compilers.buildsettings : TargetType;
@@ -72,7 +78,46 @@ PackageRecipe parsePackageRecipe(string contents, string filename, string parent
72
78
73
79
ret.name = default_package_name;
74
80
75
- if (filename.endsWith(" .json" )) parseJson(ret, parseJsonString(contents, filename), parent_name);
81
+ if (filename.endsWith(" .json" ))
82
+ {
83
+ try {
84
+ ret = parseConfigString! PackageRecipe(contents, filename, mode);
85
+ fixDependenciesNames(ret.name, ret);
86
+ } catch (ConfigException exc) {
87
+ logWarn(" Your `dub.json` file use non-conventional features that are deprecated" );
88
+ logWarn(" Please adjust your `dub.json` file as those warnings will turn into errors in dub v1.40.0" );
89
+ logWarn(" Error was: %s" , exc);
90
+ // Fallback to JSON parser
91
+ ret = PackageRecipe.init;
92
+ parseJson(ret, parseJsonString (contents, filename), parent_name);
93
+ } catch (Exception exc) {
94
+ logWarn(" Your `dub.json` file use non-conventional features that are deprecated" );
95
+ logWarn(" This is most likely due to duplicated keys." );
96
+ logWarn(" Please adjust your `dub.json` file as those warnings will turn into errors in dub v1.40.0" );
97
+ logWarn(" Error was: %s" , exc);
98
+ // Fallback to JSON parser
99
+ ret = PackageRecipe.init;
100
+ parseJson(ret, parseJsonString (contents, filename), parent_name);
101
+ }
102
+ // `debug = ConfigFillerDebug` also enables verbose parser output
103
+ debug (ConfigFillerDebug)
104
+ {
105
+ import std.stdio ;
106
+
107
+ PackageRecipe jsonret;
108
+ parseJson(jsonret, parseJsonString (contents, filename), parent_name);
109
+ if (ret != jsonret)
110
+ {
111
+ writeln(" Content of JSON and YAML parsing differ for file: " , filename);
112
+ writeln(" -------------------------------------------------------------------" );
113
+ writeln(" JSON (excepted): " , jsonret);
114
+ writeln(" -------------------------------------------------------------------" );
115
+ writeln(" YAML (actual ): " , ret);
116
+ writeln(" ========================================" );
117
+ ret = jsonret;
118
+ }
119
+ }
120
+ }
76
121
else if (filename.endsWith(" .sdl" )) parseSDL(ret, contents, parent_name, filename);
77
122
else assert (false , " readPackageRecipe called with filename with unknown extension: " ~ filename);
78
123
@@ -194,3 +239,56 @@ void serializePackageRecipe(R)(ref R dst, const scope ref PackageRecipe recipe,
194
239
toSDL(recipe).toSDLDocument(dst);
195
240
else assert (false , " writePackageRecipe called with filename with unknown extension: " ~ filename);
196
241
}
242
+
243
+ unittest {
244
+ import std.format ;
245
+ import dub.dependency;
246
+ import dub.internal.utils : deepCompare;
247
+
248
+ static void success (string source, in PackageRecipe expected, size_t line = __LINE__ ) {
249
+ const result = parseConfigString! PackageRecipe(source, " dub.json" );
250
+ deepCompare(result, expected, __FILE__ , line);
251
+ }
252
+
253
+ static void error (string source, string expected, size_t line = __LINE__ ) {
254
+ try
255
+ {
256
+ auto result = parseConfigString! PackageRecipe(source, " dub.json" );
257
+ assert (0 ,
258
+ format(" [%s:%d] Exception should have been thrown but wasn't: %s" ,
259
+ __FILE__ , line, result));
260
+ }
261
+ catch (Exception exc)
262
+ assert (exc.toString() == expected,
263
+ format(" [%s:%s] result != expected: '%s' != '%s'" ,
264
+ __FILE__ , line, exc.toString(), expected));
265
+ }
266
+
267
+ alias YAMLDep = typeof (BuildSettingsTemplate.dependencies[string .init]);
268
+ const PackageRecipe expected1 =
269
+ {
270
+ name: " foo" ,
271
+ buildSettings: {
272
+ dependencies: RecipeDependencyAA ([
273
+ " repo" : YAMLDep(Dependency(Repository(
274
+ " git+https://github.com/dlang/dmd" ,
275
+ " 09d04945bdbc0cba36f7bb1e19d5bd009d4b0ff2" ,
276
+ ))),
277
+ " path" : YAMLDep(Dependency(NativePath(" /foo/bar/jar/" ))),
278
+ " version" : YAMLDep(Dependency(VersionRange.fromString(" ~>1.0" ))),
279
+ " version2" : YAMLDep(Dependency(Version(" 4.2.0" ))),
280
+ ])},
281
+ };
282
+ success(
283
+ ` { "name": "foo", "dependencies": {
284
+ "repo": { "repository": "git+https://github.com/dlang/dmd",
285
+ "version": "09d04945bdbc0cba36f7bb1e19d5bd009d4b0ff2" },
286
+ "path": { "path": "/foo/bar/jar/" },
287
+ "version": { "version": "~>1.0" },
288
+ "version2": "4.2.0"
289
+ }}` , expected1);
290
+
291
+
292
+ error(` { "name": "bar", "dependencies": {"bad": { "repository": "git+https://github.com/dlang/dmd" }}}` ,
293
+ " dub.json(0:41): dependencies[bad]: Need to provide a commit hash in 'version' field with 'repository' dependency" );
294
+ }
0 commit comments