1 module tests.it.runtime.dub;
2 
3 
4 import tests.it.runtime;
5 import reggae.reggae;
6 import reggae.path: buildPath, deabsolutePath, dubPackagesDir;
7 
8 
9 @("noreggaefile.ninja")
10 @Tags(["dub", "ninja"])
11 unittest {
12 
13     import std..string: join;
14     import std.algorithm: filter;
15 
16     with(immutable ReggaeSandbox("dub")) {
17         shouldNotExist("reggaefile.d");
18         writelnUt("\n\nReggae output:\n\n", runReggae("-b", "ninja").lines.join("\n"), "-----\n");
19         shouldExist("reggaefile.d");
20         auto output = ninja(["-v"]).shouldExecuteOk;
21 
22         version(Windows) {
23             // args in response file
24         } else {
25             output.shouldContain("-debug -g");
26         }
27 
28         shouldSucceed("atest").filter!(a => a != "").should ==
29             [
30                 "Why hello!",
31                 "I'm immortal!"
32             ];
33 
34         // there's only one UT in main.d which always fails
35         ninja(["ut"]).shouldExecuteOk;
36         shouldFail("ut");
37     }
38 }
39 
40 @("noreggaefile.tup")
41 @Tags(["dub", "tup"])
42 unittest {
43     with(immutable ReggaeSandbox("dub")) {
44         runReggae("-b", "tup").
45             shouldThrowWithMessage("dub integration not supported with the tup backend");
46     }
47 }
48 
49 
50 @("prebuild")
51 @Tags(["dub", "ninja"])
52 unittest {
53     with(immutable ReggaeSandbox("dub_prebuild")) {
54         runReggae("-b", "ninja");
55         ninja(["default", "ut"]).shouldExecuteOk;
56         shouldSucceed("ut");
57     }
58 }
59 
60 
61 @("postbuild")
62 @Tags(["dub", "ninja", "posix"])
63 unittest {
64     with(immutable ReggaeSandbox("dub_postbuild")) {
65         runReggae("-b", "ninja");
66         ninja.shouldExecuteOk;
67         shouldExist("foo.txt");
68         shouldSucceed("postbuild");
69     }
70 }
71 
72 
73 @("dependencies not on file system already no dub.selections.json")
74 @Tags(["dub", "ninja"])
75 unittest {
76 
77     import std.file: exists, rmdirRecurse;
78     import std.process: environment;
79 
80     const cerealedDir = buildPath(dubPackagesDir(), "cerealed-0.6.8");
81     if(cerealedDir.exists)
82         rmdirRecurse(cerealedDir);
83 
84     with(immutable ReggaeSandbox()) {
85         writeFile("dub.json", `
86         {
87           "name": "depends_on_cerealed",
88           "license": "MIT",
89           "targetType": "executable",
90           "dependencies": { "cerealed": "==0.6.8" }
91         }`);
92         writeFile("source/app.d", "void main() {}");
93 
94         runReggae("-b", "ninja");
95     }
96 }
97 
98 
99 @("no main function but with unit tests")
100 @Tags(["dub", "ninja"])
101 unittest {
102     import std.file: mkdirRecurse;
103 
104     with(immutable ReggaeSandbox()) {
105         writeFile("dub.json", `
106             {
107               "name": "depends_on_cerealed",
108               "license": "MIT",
109               "targetType": "executable",
110               "dependencies": { "cerealed": "==0.6.8" }
111             }`);
112 
113         writeFile("reggaefile.d", q{
114             import reggae;
115             mixin build!(dubTestTarget!());
116         });
117 
118         mkdirRecurse(buildPath(testPath, "source"));
119         writeFile("source/foo.d", `unittest { assert(false); }`);
120         runReggae("-b", "ninja");
121         ninja.shouldExecuteOk;
122 
123         shouldFail("ut");
124     }
125 }
126 
127 
128 @("reggae/dub build should rebuild if dub.selections.json changes")
129 @Tags(["dub", "make"])
130 unittest {
131 
132     import std.process: execute;
133 
134     with(immutable ReggaeSandbox("dub")) {
135         runReggae("-b", "make");
136         make(["VERBOSE=1"]).shouldExecuteOk.shouldContain("-debug -g");
137         {
138             const ret = execute(["touch", buildPath(testPath, "dub.selections.json")]);
139             ret.status.shouldEqual(0);
140         }
141         {
142             const ret = execute(["make", "-C", testPath]);
143             ret.output.shouldContain("eggae");
144         }
145     }
146 }
147 
148 @("version from main package is used in dependent packages")
149 @Tags(["dub", "ninja"])
150 unittest {
151     with(immutable ReggaeSandbox()) {
152         writeFile("dub.sdl", `
153             name "foo"
154             versions "lefoo"
155             targetType "executable"
156             dependency "bar" path="bar"
157         `);
158         writeFile("source/app.d", q{
159             void main() {
160                 import bar;
161                 import std.stdio;
162                 writeln(lebar);
163             }
164         });
165         writeFile("bar/dub.sdl", `
166             name "bar"
167         `);
168         writeFile("bar/source/bar.d", q{
169             module bar;
170             version(lefoo)
171                 int lebar() { return 3; }
172             else
173                 int lebar() { return 42; }
174         });
175         runReggae("-b", "ninja");
176         ninja.shouldExecuteOk;
177         shouldSucceed("foo").shouldEqual(
178             [
179                 "3",
180             ]
181         );
182     }
183 }
184 
185 
186 @("sourceLibrary dependency")
187 @Tags(["dub", "ninja"])
188 unittest {
189     with(immutable ReggaeSandbox()) {
190         writeFile("dub.sdl", `
191             name "foo"
192             targetType "executable"
193             dependency "bar" path="bar"
194         `);
195         writeFile("source/app.d", q{
196             void main() {
197                 import bar;
198                 import std.stdio;
199                 writeln(lebar);
200             }
201         });
202         writeFile("bar/dub.sdl", `
203             name "bar"
204             targetType "sourceLibrary"
205         `);
206         writeFile("bar/source/bar.d", q{
207             module bar;
208             int lebar() { return 3; }
209         });
210         runReggae("-b", "ninja");
211         ninja.shouldExecuteOk;
212     }
213 }
214 
215 version(Windows) version(DigitalMars) version = Windows_DMD;
216 
217 version(Windows_DMD) {
218     /**
219      * On Windows, DMD defaults to -m32, reggae to -m32mscoff (for DMD), and
220      * dub to -m64 (if run on a 64-bit Windows host, otherwise -m32mscoff) for
221      * compilers with DMD CLI (dmd, gdmd, ldmd2). ;)
222      * Windows_DMD assumes a 32-bit MSVC environment (cl.exe etc.) for the
223      * tests, so specify the corresponding dub architecture in the reggae
224      * cmdline.
225      */
226     enum dubArch = "--dub-arch=x86_mscoff";
227 } else {
228     enum string dubArch = null;
229 }
230 
231 version(DigitalMars) {
232     @("object source files.simple")
233     @Tags(["dub", "ninja"])
234     unittest {
235         with(immutable ReggaeSandbox()) {
236             writeFile("dub.sdl", `
237                 name "foo"
238                 targetType "executable"
239                 dependency "bar" path="bar"
240             `);
241             writeFile("source/app.d", q{
242                 extern(C) int lebaz();
243                 void main() {
244                     import bar;
245                     import std.stdio;
246                     writeln(lebar);
247                     writeln(lebaz);
248                 }
249             });
250             writeFile("bar/dub.sdl", `
251                 name "bar"
252                 sourceFiles "../baz.o" platform="posix"
253                 sourceFiles "../baz.obj" platform="windows"
254             `);
255             writeFile("bar/source/bar.d", q{
256                 module bar;
257                 int lebar() { return 3; }
258             });
259             writeFile("baz.d", q{
260                 module baz;
261                 extern(C) int lebaz() { return 42; }
262             });
263 
264             version(Windows_DMD)
265                 ["dmd", "-m32mscoff", "-c", "baz.d"].shouldExecuteOk;
266             else
267                 ["dmd", "-c", "baz.d"].shouldExecuteOk;
268 
269             runReggae("-b", "ninja", dubArch);
270             ninja.shouldExecuteOk;
271         }
272     }
273 }
274 
275 
276 @("dub objs option path dependency")
277 @Tags("dub", "ninja", "dubObjsDir")
278 unittest {
279 
280     with(immutable ReggaeSandbox()) {
281 
282         writeFile("reggaefile.d", q{
283             import reggae;
284             mixin build!(dubDefaultTarget!());
285         });
286 
287         writeFile("dub.sdl",`
288             name "foo"
289             targetType "executable"
290             dependency "bar" path="bar"
291         `);
292 
293         writeFile("source/app.d", q{
294             import bar;
295             void main() { add(2, 3); }
296         });
297 
298         writeFile("bar/dub.sdl", `
299             name "bar"
300         `);
301 
302         writeFile("bar/source/bar.d", q{
303             module bar;
304             int add(int i, int j) { return i + j; }
305         });
306 
307         const dubObjsDir = buildPath(testPath, "objsdir");
308         const output = runReggae("-b", "ninja", "--dub-objs-dir=" ~ dubObjsDir, "--dub-deps-objs");
309         writelnUt(output);
310         ninja.shouldExecuteOk;
311 
312         shouldExist(buildPath("objsdir",
313                               testPath.deabsolutePath,
314                               "foo" ~ exeExt ~ ".objs",
315                               testPath.deabsolutePath,
316                               "bar",
317                               "source_bar" ~ objExt));
318     }
319 }
320 
321 
322 @("dub objs option registry dependency")
323 @Tags("dub", "ninja", "dubObjsDir")
324 unittest {
325 
326     import reggae.path: dubPackagesDir, deabsolutePath;
327 
328     with(immutable ReggaeSandbox()) {
329 
330         writeFile("reggaefile.d", q{
331             import reggae;
332             mixin build!(dubDefaultTarget!());
333         });
334 
335         writeFile("dub.sdl",`
336             name "foo"
337             targetType "executable"
338             dependency "dubnull" version="==0.0.1"
339         `);
340 
341         writeFile("source/app.d", q{
342             import dubnull;
343             void main() { dummy(); }
344         });
345 
346         const dubObjsDir = buildPath(testPath, "objsdir");
347         const output = runReggae("-b", "ninja", "--dub-objs-dir=" ~ dubObjsDir, "--dub-deps-objs");
348         writelnUt(output);
349 
350         ninja.shouldExecuteOk;
351 
352         const dubNullDir = buildPath(dubPackagesDir, "dubnull-0.0.1/dubnull").deabsolutePath;
353         shouldExist(buildPath("objsdir",
354                               testPath.deabsolutePath,
355                               "foo" ~ exeExt ~ ".objs",
356                               dubNullDir,
357                               "source_dubnull" ~ objExt));
358     }
359 }
360 
361 version(DigitalMars) {
362     @("object source files.with dub objs option")
363     @Tags("dub", "ninja", "dubObjsDir")
364     unittest {
365         with(immutable ReggaeSandbox()) {
366             writeFile("dub.sdl", `
367                 name "foo"
368                 targetType "executable"
369                 dependency "bar" path="bar"
370             `);
371             writeFile("source/app.d", q{
372                 extern(C) int lebaz();
373                 void main() {
374                     import bar;
375                     import std.stdio;
376                     writeln(lebar);
377                     writeln(lebaz);
378                 }
379             });
380             writeFile("bar/dub.sdl", `
381                 name "bar"
382                 sourceFiles "../baz.o" platform="posix"
383                 sourceFiles "../baz.obj" platform="windows"
384             `);
385             writeFile("bar/source/bar.d", q{
386                 module bar;
387                 int lebar() { return 3; }
388             });
389             writeFile("baz.d", q{
390                 module baz;
391                 extern(C) int lebaz() { return 42; }
392             });
393 
394             version(Windows_DMD) {
395                 ["dmd", "-m32mscoff", "-c", "baz.d"].shouldExecuteOk;
396             } else {
397                 ["dmd", "-c", "baz.d"].shouldExecuteOk;
398             }
399 
400             const output = runReggae("-b", "ninja", "--dub-objs-dir=" ~ testPath, dubArch);
401             writelnUt(output);
402 
403             ninja.shouldExecuteOk;
404         }
405     }
406 }
407 
408 
409 @("dub with spaces")
410 @Tags("dub", "ninja")
411 unittest {
412     /* Use Ninja to build a tiny dub project with spaces in:
413      * - source directory
414      * - source filename
415      * - import directory
416      * - compiler flags
417      * - linker flags
418      * - output filename
419      */
420     with(immutable ReggaeSandbox()) {
421         writeFile("dub.sdl", `
422             name "dub_with_spaces"
423             targetName "dub with spaces"
424             targetType "executable"
425 
426             sourcePaths "my source"
427             importPaths "my source"
428             mainSourceFile "my source/my module.d"
429 
430             dflags "-L-Lmy libs" platform="posix"
431             dflags "-L/LIBPATH:my libs" platform="windows"
432 
433             lflags "-Lmy other libs" platform="posix"
434             lflags "/LIBPATH:my other libs" platform="windows"
435         `);
436 
437         writeFile("my source/my module.d", q{
438             module my_module;
439             import other_module;
440             void main() { foo(); }
441         });
442 
443         writeFile("my source/other_module.d", q{
444             void foo() {}
445         });
446 
447         // use --per-module to test that Ninja accepts the .dep files
448         // generated for both modules/objects
449         const output = runReggae("-b", "ninja", "--per-module");
450         writelnUt(output);
451 
452         ninja.shouldExecuteOk;
453 
454         shouldExist("dub with spaces" ~ exeExt);
455     }
456 }
457 
458 
459 @("depends on package with prebuild")
460 @Tags(["dub", "ninja"])
461 unittest {
462 
463     with(immutable ReggaeSandbox("dub_depends_on_prebuild")) {
464 
465         copyProject("dub_prebuild", buildPath("../dub_prebuild"));
466 
467         runReggae("-b", "ninja");
468         ninja.shouldExecuteOk;
469         shouldSucceed("app");
470         shouldExist(inSandboxPath("../dub_prebuild/el_prebuildo.txt"));
471     }
472 }
473 
474 
475 @("staticLibrary.implicit")
476 @Tags(["dub", "ninja"])
477 unittest {
478 
479     with(immutable ReggaeSandbox()) {
480         writeFile("dub.sdl", `
481             name "foo"
482             targetType "executable"
483             targetName "d++"
484 
485             configuration "executable" {
486             }
487 
488             configuration "library" {
489                 targetType "library"
490                 targetName "dpp"
491                 excludedSourceFiles "source/main.d"
492             }
493         `);
494 
495         writeFile("reggaefile.d",
496                   q{
497                       import reggae;
498                       alias lib = dubConfigurationTarget!(Configuration("library"));
499                       enum mainObj = objectFile(SourceFile("source/main.d"));
500                       alias exe = link!(ExeName("d++"), targetConcat!(lib, mainObj));
501                       mixin build!(exe);
502                   });
503 
504         writeFile("source/main.d", "void main() {}");
505         writeFile("source/foo/bar/mod.d", "module foo.bar.mod; int add1(int i, int j) { return i + j + 1; }");
506 
507         runReggae("-b", "ninja");
508         ninja.shouldExecuteOk;
509         shouldSucceed("d++");
510     }
511 }
512 
513 
514 @("staticLibrary.explicit")
515 @Tags(["dub", "ninja"])
516 unittest {
517 
518     with(immutable ReggaeSandbox()) {
519         writeFile("dub.sdl", `
520             name "foo"
521             targetType "executable"
522             targetName "d++"
523 
524             configuration "executable" {
525             }
526 
527             configuration "library" {
528                 targetType "staticLibrary"
529                 targetName "dpp"
530                 excludedSourceFiles "source/main.d"
531             }
532         `);
533 
534         writeFile("reggaefile.d",
535                   q{
536                       import reggae;
537                       alias lib = dubConfigurationTarget!(Configuration("library"));
538                       enum mainObj = objectFile(SourceFile("source/main.d"));
539                       alias exe = link!(ExeName("d++"), targetConcat!(lib, mainObj));
540                       mixin build!(exe);
541                   });
542 
543         writeFile("source/main.d", "void main() {}");
544         writeFile("source/foo/bar/mod.d", "module foo.bar.mod; int add1(int i, int j) { return i + j + 1; }");
545 
546         runReggae("-b", "ninja");
547         ninja.shouldExecuteOk;
548         shouldSucceed("d++");
549     }
550 }
551 
552 
553 @("failing prebuild command")
554 @Tags(["dub", "ninja"])
555 unittest {
556     with(immutable ReggaeSandbox("dub_prebuild_oops")) {
557         auto thrownInfo = runReggae("-b", "ninja").shouldThrow;
558         "Error calling foo bar baz quux:".should.be in thrownInfo.msg;
559         version(Windows) {
560             enum expectedDetail = "'foo' is not recognized as an internal or external command";
561         } else {
562             enum expectedDetail = "not found";
563         }
564         expectedDetail.should.be in thrownInfo.msg;
565     }
566 }
567 
568 
569 @("libs.plain")
570 @Tags(["dub", "ninja"])
571 unittest {
572     with(immutable ReggaeSandbox()) {
573         writeFile("dub.sdl", `
574             name "foo"
575             targetType "executable"
576             libs "utils"
577             lflags "-L$PACKAGE_DIR" platform="posix"
578             lflags "/LIBPATH:$PACKAGE_DIR" platform="windows"
579 
580             configuration "executable" {
581             }
582 
583             configuration "library" {
584                 targetType "library"
585                 targetName "dpp"
586                 excludedSourceFiles "source/main.d"
587             }
588         `);
589 
590         writeFile("reggaefile.d",
591                   q{
592                       import reggae;
593                       alias exe = dubDefaultTarget!(
594                       );
595                       mixin build!(exe);
596                   });
597 
598         writeFile("source/main.d",
599                   q{
600                       extern(C) int twice(int);
601                       void main() {
602                           assert(twice(2) == 4);
603                           assert(twice(3) == 6);
604                       }
605                   });
606 
607         writeFile("utils.c", "int twice(int i) { return i * 2; }");
608         version(Windows) {
609             shouldExecuteOk(["cl.exe", "/Fo" ~ inSandboxPath("utils.obj"), "/c", inSandboxPath("utils.c")]);
610             shouldExecuteOk(["lib.exe", "/OUT:" ~ inSandboxPath("utils.lib"), inSandboxPath("utils.obj")]);
611         } else {
612             shouldExecuteOk(["gcc", "-o", inSandboxPath("utils.o"), "-c", inSandboxPath("utils.c")]);
613             shouldExecuteOk(["ar", "rcs", inSandboxPath("libutils.a"), inSandboxPath("utils.o")]);
614         }
615 
616         runReggae("-b", "ninja", dubArch);
617         ninja.shouldExecuteOk;
618         shouldSucceed("foo");
619     }
620 }
621 
622 
623 @("libs.posix")
624 @Tags(["dub", "ninja"])
625 unittest {
626     with(immutable ReggaeSandbox()) {
627         writeFile("dub.sdl", `
628             name "foo"
629             targetType "executable"
630             libs "utils"
631             lflags "-L$PACKAGE_DIR" platform="posix"
632             lflags "/LIBPATH:$PACKAGE_DIR" platform="windows"
633 
634             configuration "executable" {
635             }
636 
637             configuration "library" {
638                 targetType "library"
639                 targetName "dpp"
640                 excludedSourceFiles "source/main.d"
641             }
642         `);
643 
644         writeFile("reggaefile.d",
645                   q{
646                       import reggae;
647                       alias exe = dubDefaultTarget!(
648                       );
649                       mixin build!(exe);
650                   });
651 
652         writeFile("source/main.d",
653                   q{
654                       extern(C) int twice(int);
655                       void main() {
656                           assert(twice(2) == 4);
657                           assert(twice(3) == 6);
658                       }
659                   });
660 
661         writeFile("utils.c", "int twice(int i) { return i * 2; }");
662         version(Windows) {
663             shouldExecuteOk(["cl.exe", "/Fo" ~ inSandboxPath("utils.obj"), "/c", inSandboxPath("utils.c")]);
664             shouldExecuteOk(["lib.exe", "/OUT:" ~ inSandboxPath("utils.lib"), inSandboxPath("utils.obj")]);
665         } else {
666             shouldExecuteOk(["gcc", "-o", inSandboxPath("utils.o"), "-c", inSandboxPath("utils.c")]);
667             shouldExecuteOk(["ar", "rcs", inSandboxPath("libutils.a"), inSandboxPath("utils.o")]);
668         }
669 
670         runReggae("-b", "ninja", dubArch);
671         ninja.shouldExecuteOk;
672         shouldSucceed("foo");
673     }
674 }
675 
676 
677 @("libs.dependency")
678 @Tags(["dub", "ninja"])
679 unittest {
680     with(immutable ReggaeSandbox()) {
681         writeFile("dub.sdl", `
682             name "foo"
683             targetType "executable"
684             dependency "bar" path="bar"
685         `);
686 
687         writeFile("reggaefile.d",
688                   q{
689                       import reggae;
690                       mixin build!(dubDefaultTarget!());
691                   });
692 
693         writeFile("source/main.d",
694                   q{
695                       import bar;
696                       void main() {
697                           assert(times4(2) == 8);
698                           assert(times4(3) == 12);
699                       }
700                   });
701 
702         writeFile("bar/dub.sdl", `
703             name "bar"
704             targetType "library"
705             lflags "-L$PACKAGE_DIR" platform="posix"
706             lflags "/LIBPATH:$PACKAGE_DIR" platform="windows"
707             libs "utils"
708         `);
709 
710         writeFile("bar/source/bar.d", q{
711                 module bar;
712                 extern(C) int twice(int);
713                 int times4(int i) { return 2 * twice(i); }
714             }
715         );
716 
717         writeFile("bar/utils.c", "int twice(int i) { return i * 2; }");
718         version(Windows) {
719             shouldExecuteOk(["cl.exe", "/Fo" ~ inSandboxPath("bar/utils.obj"), "/c", inSandboxPath("bar/utils.c")]);
720             shouldExecuteOk(["lib.exe", "/OUT:" ~ inSandboxPath("bar/utils.lib"), inSandboxPath("bar/utils.obj")]);
721         } else {
722             shouldExecuteOk(["gcc", "-o", inSandboxPath("bar/utils.o"), "-c", inSandboxPath("bar/utils.c")]);
723             shouldExecuteOk(["ar", "rcs", inSandboxPath("bar/libutils.a"), inSandboxPath("bar/utils.o")]);
724         }
725 
726         runReggae("-b", "ninja", dubArch);
727         ninja.shouldExecuteOk;
728         shouldSucceed("foo");
729     }
730 }
731 
732 
733 @("dflags.debug")
734 @Tags("dub", "ninja")
735 unittest {
736     with(immutable ReggaeSandbox()) {
737         writeFile("dub.sdl", `
738             name "foo"
739             targetType "executable"
740         `);
741 
742         writeFile("source/main.d",
743                   q{
744                       void main() {
745                           debug assert(false);
746                       }
747                   });
748 
749         runReggae("-b", "ninja");
750         ninja.shouldExecuteOk;
751         shouldFail("foo");
752     }
753 }
754 
755 
756 @("unittest.dependency")
757 @Tags(["dub", "ninja"])
758 unittest {
759     with(immutable ReggaeSandbox()) {
760         writeFile("dub.sdl", `
761             name "foo"
762             targetType "executable"
763             dependency "bar" path="bar"
764         `);
765         writeFile("source/app.d", q{
766             void main() {
767             }
768         });
769         writeFile("bar/dub.sdl", `
770             name "bar"
771         `);
772         writeFile("bar/source/bar.d", q{
773             module bar;
774             unittest {
775                 assert(1 == 2);
776             }
777         });
778         runReggae("-b", "ninja");
779         ninja(["default", "ut"]).shouldExecuteOk;
780         shouldSucceed("ut");
781     }
782 }
783 
784 
785 @("unittest.self")
786 @Tags(["dub", "ninja"])
787 unittest {
788     with(immutable ReggaeSandbox()) {
789         writeFile("dub.sdl", `
790             name "foo"
791             targetType "executable"
792         `);
793         writeFile("source/app.d", q{
794             void main() {
795             }
796 
797             unittest { assert(1 == 2); }
798         });
799         runReggae("-b", "ninja");
800         ninja(["default", "ut"]).shouldExecuteOk;
801         shouldFail("ut");
802     }
803 }
804 
805 
806 @("subpackages")
807 @Tags(["dub", "ninja"])
808 unittest {
809     with(immutable ReggaeSandbox()) {
810         writeFile("dub.json", `
811             {
812                 "name": "oops",
813                 "targetType": "none",
814                 "subPackages": [
815                     {
816                         "name": "pkg1",
817                         "targetType": "staticLibrary"
818                     },
819                     {
820                         "name": "pkg2",
821                         "targetType": "executable",
822                         "sourceFiles": ["main.d"],
823                         "dependencies": {
824                             "oops:pkg1": "*"
825                         }
826                     }
827                 ],
828                 "dependencies": {
829                     "oops:pkg1": "*",
830                     "oops:pkg2": "*"
831                 }
832             }
833         `);
834         writeFile("main.d", q{
835             void main() {
836                 import oops;
837                 import std.stdio;
838                 writeln(3.twice);
839             }
840         });
841         writeFile("source/oops.d", q{
842             module oops;
843             int twice(int i) { return i * 2; }
844         });
845 
846         runReggae("-b", "ninja");
847         ninja(["default", "ut"]).shouldExecuteOk;
848         shouldFail("ut");
849     }
850 }
851 
852 
853 @("buildtype.release")
854 @Tags("dub", "ninja")
855 unittest {
856 
857     import std..string: splitLines;
858 
859     with(immutable ReggaeSandbox()) {
860         writeFile(
861             "dub.sdl",
862             [
863                 `name "foo"`,
864                 `targetType "executable"`,
865             ],
866         );
867         writeFile(
868             "source/app.d",
869             [
870                 q{void main() {}},
871             ],
872         );
873 
874         runReggae("-b", "ninja", "--dub-build-type=release");
875         const buildLines = ninja(["-v"]).shouldExecuteOk;
876 
877         version(Windows) {
878             // args in response file
879         } else {
880             const firstLine = buildLines[0];
881             "-release ".should.be in firstLine;
882             "-O".should.be in firstLine;
883         }
884     }
885 
886 }