1 module dlangide.builders.builder;
2 
3 import dlangui.core.logger;
4 import dlangide.workspace.project;
5 import dlangide.workspace.workspace;
6 import dlangide.ui.outputpanel;
7 import dlangide.builders.extprocess;
8 import dlangui.widgets.appframe;
9 import std.algorithm;
10 import std.array;
11 import core.thread;
12 import std.string;
13 import std.conv;
14 
15 alias BuildResultListener = void delegate(int);
16 
17 class Builder : BackgroundOperationWatcher {
18     protected Project _project;
19     protected ExternalProcess _extprocess;
20     protected OutputPanel _log;
21     protected ProtectedTextStorage _box;
22     protected ProjectConfiguration _projectConfig;
23     protected BuildConfiguration _buildConfig;
24     protected BuildOperation _buildOp;
25     protected bool _verbose;
26     protected BuildResultListener _listener;
27     protected int _exitCode = int.min;
28     protected string _toolchain;
29     protected string _arch;
30 
31     @property Project project() { return _project; }
32     @property void project(Project p) { _project = p; }
33 
34     this(AppFrame frame, Project project, OutputPanel log, ProjectConfiguration projectConfig, BuildConfiguration buildConfig, 
35              BuildOperation buildOp, bool verbose, 
36              string toolchain = null,
37              string arch = null,
38              BuildResultListener listener = null) {
39         super(frame);
40         _listener = listener;
41         _projectConfig = projectConfig;
42         _buildConfig = buildConfig;
43         _buildOp = buildOp;
44         _verbose = verbose;
45         _project = project;
46         _log = log;
47         _toolchain = toolchain;
48         _arch = arch;
49         _extprocess = new ExternalProcess();
50         _box = new ProtectedTextStorage();
51     }
52 
53     /// log lines
54     void pollText() {
55         dstring text = _box.readText();
56         if (text.length) {
57             _log.appendText(null, text);
58         }
59     }
60 
61     /// returns icon of background operation to show in status line
62     override @property string icon() { return "folder"; }
63     /// update background operation status
64     override void update() {
65         scope(exit)pollText();
66         ExternalProcessState state = _extprocess.state;
67         if (state == ExternalProcessState.None) {
68             _log.clear();
69             _box.writeText("Running dub\n"d);
70             char[] program = "dub".dup;
71             char[][] params;
72             char[] dir = _project.dir.dup;
73 
74             if (_buildOp == BuildOperation.Build || _buildOp == BuildOperation.Rebuild) {
75                 params ~= "build".dup;
76                 if (_buildOp == BuildOperation.Rebuild) {
77                     params ~= "--force".dup;
78                 }
79                 if (!_arch.empty)
80                     params ~= ("--arch=" ~ _arch).dup;
81                 if (!_toolchain.empty)
82                     params ~= ("--compiler=" ~ _toolchain).dup;
83                 params ~= "--build-mode=allAtOnce".dup;
84             } else if (_buildOp == BuildOperation.Clean) {
85                 params ~= "clean".dup;
86             } else if (_buildOp == BuildOperation.Run) {
87                 if (_projectConfig.type == ProjectConfiguration.Type.Library) {
88                     params ~= "test".dup;
89                 } else {
90                 	params ~= "run".dup;
91             	}
92             } else if (_buildOp == BuildOperation.Upgrade) {
93                 params ~= "upgrade".dup;
94                 params ~= "--force-remove".dup;
95             }
96 
97             if (_buildOp != BuildOperation.Clean && _buildOp != BuildOperation.Upgrade) {
98                 switch (_buildConfig) {
99                     default:
100                     case BuildConfiguration.Debug:
101                         params ~= "--build=debug".dup;
102                         break;
103                     case BuildConfiguration.Release:
104                         params ~= "--build=release".dup;
105                         break;
106                     case BuildConfiguration.Unittest:
107                         params ~= "--build=unittest".dup;
108                         break;
109                 }
110             }
111 
112             if(_projectConfig.name != ProjectConfiguration.DEFAULT_NAME) {
113                 params ~= "--config=".dup ~ _projectConfig.name;
114             }
115             
116             if (_verbose)
117                 params ~= "-v".dup;
118 
119             state = _extprocess.run(program, params, dir, _box, null);
120             if (state != ExternalProcessState.Running) {
121                 _box.writeText("Failed to run builder tool\n"d);
122                 _finished = true;
123                 destroy(_extprocess);
124                 _extprocess = null;
125                 return;
126             }
127         }
128         state = _extprocess.poll();
129         if (state == ExternalProcessState.Stopped) {
130             _exitCode = _extprocess.result;
131             _box.writeText("Builder finished with result "d ~ to!dstring(_extprocess.result) ~ "\n"d);
132             _finished = true;
133             return;
134         }
135         if (_cancelRequested) {
136             _extprocess.kill();
137             _extprocess.wait();
138             _finished = true;
139             return;
140         }
141         super.update();
142     }
143     override void removing() {
144         super.removing();
145         if (_exitCode != int.min && _listener)
146             _listener(_exitCode);
147     }
148 }