1 /**************************************************************************
2 ** This file is part of LiteIDE
3 **
4 ** Copyright (c) 2011-2019 LiteIDE. All rights reserved.
5 **
6 ** This library is free software; you can redistribute it and/or
7 ** modify it under the terms of the GNU Lesser General Public
8 ** License as published by the Free Software Foundation; either
9 ** version 2.1 of the License, or (at your option) any later version.
10 **
11 ** This library is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ** Lesser General Public License for more details.
15 **
16 ** In addition, as a special exception,  that plugins developed for LiteIDE,
17 ** are allowed to remain closed sourced and can be distributed under any license .
18 ** These rights are included in the file LGPL_EXCEPTION.txt in this package.
19 **
20 **************************************************************************/
21 // Module: liteenvapi.h
22 // Creator: visualfc <visualfc@gmail.com>
23 
24 #ifndef LITEENVAPI_H
25 #define LITEENVAPI_H
26 
27 #include "liteapi/liteapi.h"
28 #include <QProcessEnvironment>
29 #include <QDir>
30 #include <QDebug>
31 
32 namespace LiteApi {
33 
34 class IEnv : public QObject
35 {
36     Q_OBJECT
37 public:
QObject(parent)38     IEnv(QObject *parent = 0): QObject(parent) {}
~IEnv()39     virtual ~IEnv() {}
40     virtual QString id() const = 0;
41     virtual QString filePath() const = 0;
42     virtual QStringList orgEnvLines() const = 0;
43     virtual QMap<QString,QString> goEnvMap() const = 0;
44     virtual QProcessEnvironment& environment() = 0;
45     virtual void reload() = 0;
46 signals:
47     void goenvError(QString,QString);
48     void goenvChanged(QString);
49 };
50 
51 class IEnvManager : public IManager
52 {
53     Q_OBJECT
54 public:
IManager(parent)55     IEnvManager(QObject *parent = 0) : IManager(parent) {}
56     virtual QList<IEnv*> envList() const = 0;
57     virtual IEnv *findEnv(const QString &id, const QString &backup = "system") const = 0;
58     virtual void setCurrentEnvId(const QString &id) = 0;
59     virtual IEnv *currentEnv() const = 0;
60     virtual QProcessEnvironment currentEnvironment() const = 0;
61     virtual void reloadCurrentEnv() = 0;
62 signals:
63     void currentEnvChanged(LiteApi::IEnv*);
64 };
65 
66 class IGoEnvManger: public IManager
67 {
68     Q_OBJECT
69 public:
IManager(parent)70     IGoEnvManger(QObject *parent = 0) : IManager(parent) {}
71     virtual QString gocmd() const = 0;
72     virtual QString gotools() const = 0;
73     virtual QString GOROOT() const = 0;
74     virtual QStringList GOPATH() const = 0;
75     virtual QProcessEnvironment environment() const = 0;
76     virtual QProcessEnvironment customEnvironment(const QString &buildFilePath, QString *pCustomBuildPath = 0) const = 0;
77     virtual QStringList customGOPATH(const QString &buildPath, QString *pCustomBuildPath = 0) const = 0;
78     virtual QString findRealCustomBuildPath(const QString &buildPath) const = 0;
79     virtual bool hasCustomGOPATH(const QString &buildPath) const = 0;
80     virtual void updateGoEnv() = 0;
81     virtual void updateCustomGOPATH(const QString &buildPath) = 0;
82 signals:
83     void globalGOPATHChanged();
84     void customGOPATHChanged(const QString &buildPath);
85 };
86 
getEnvManager(LiteApi::IApplication * app)87 inline IEnvManager *getEnvManager(LiteApi::IApplication* app)
88 {
89     return LiteApi::findExtensionObject<IEnvManager*>(app,"LiteApi.IEnvManager");
90 }
91 
getGoEnvManager(LiteApi::IApplication * app)92 inline IGoEnvManger *getGoEnvManager(LiteApi::IApplication *app)
93 {
94     return LiteApi::findExtensionObject<IGoEnvManger*>(app,"LiteApi.IGoEnvManger");
95 }
96 
getCurrentEnvironment(LiteApi::IApplication * app)97 inline QProcessEnvironment getCurrentEnvironment(LiteApi::IApplication *app)
98 {
99     QProcessEnvironment e;
100     IEnvManager *env = getEnvManager(app);
101     if (env) {
102         e = env->currentEnvironment();
103     } else {
104         e = QProcessEnvironment::systemEnvironment();
105     }
106 #ifdef Q_OS_WIN
107     QString sep = ";";
108 #else
109     QString sep = ":";
110 #endif
111     QStringList pathList;
112     foreach (QString path, e.value("PATH").split(sep,QString::SkipEmptyParts)) {
113         pathList.append(QDir::toNativeSeparators(path));
114     }
115     pathList.append(app->applicationPath());
116     pathList.removeDuplicates();
117     e.insert("PATH",pathList.join(sep));
118     return e;
119 }
120 
getDefaultGOOS()121 inline QString getDefaultGOOS()
122 {
123     const char* goos = "";
124 #ifdef Q_OS_WIN
125     goos = "windows";
126 #endif
127 #ifdef Q_OS_LINUX
128     goos = "linux";
129 #endif
130 #ifdef Q_OS_DARWIN
131     goos = "darwin";
132 #endif
133 #ifdef Q_OS_FREEBSD
134     goos = "freebsd";
135 #endif
136 #ifdef Q_OS_OPENBSD
137     goos = "openbsd";
138 #endif
139 	return goos;
140 }
141 
getDefaultGOROOT()142 inline QString getDefaultGOROOT()
143 {
144 #ifdef Q_OS_WIN
145     return "c:\\go";
146 #else
147     return "/usr/local/go";
148 #endif
149 }
150 
hasGoEnv(const QProcessEnvironment & env)151 inline bool hasGoEnv(const QProcessEnvironment &env)
152 {
153     return env.contains("GOROOT") && env.contains("GOARCH");
154 }
155 
getSysEnvironment(LiteApi::IApplication * app)156 inline QProcessEnvironment getSysEnvironment(LiteApi::IApplication *app)
157 {
158     QProcessEnvironment env = getCurrentEnvironment(app);
159 #ifdef Q_OS_WIN
160     QString sep = ";";
161 #else
162     QString sep = ":";
163 #endif
164 
165     IEnvManager *mgr = LiteApi::getEnvManager(app);
166     if (mgr) {
167         LiteApi::IEnv *ce = mgr->currentEnv();
168         if (ce) {
169             QMapIterator<QString,QString> i(ce->goEnvMap());
170             while(i.hasNext()) {
171                 i.next();
172                 env.insert(i.key(),i.value());
173             }
174         }
175     }
176 
177     QString goos = env.value("GOOS");
178     if (goos.isEmpty()) {
179         goos = getDefaultGOOS();
180     }
181 
182     QString goroot = env.value("GOROOT");
183     if (goroot.isEmpty()) {
184         goroot = getDefaultGOROOT();
185     }
186     return env;
187 }
188 
189 
getGoEnvironment(LiteApi::IApplication * app)190 inline QProcessEnvironment getGoEnvironment(LiteApi::IApplication *app)
191 {
192     QProcessEnvironment env = getCurrentEnvironment(app);
193 #ifdef Q_OS_WIN
194     QString sep = ";";
195 #else
196     QString sep = ":";
197 #endif
198 
199     IEnvManager *mgr = LiteApi::getEnvManager(app);
200     if (mgr) {
201         LiteApi::IEnv *ce = mgr->currentEnv();
202         if (ce) {
203             QMapIterator<QString,QString> i(ce->goEnvMap());
204             while(i.hasNext()) {
205                 i.next();
206                 env.insert(i.key(),i.value());
207             }
208         }
209     }
210 
211     QString goos = env.value("GOOS");
212     if (goos.isEmpty()) {
213         goos = getDefaultGOOS();
214     }
215     if (!env.contains("GOEXE")) {
216         QString goexe;
217         if (goos == "windows") {
218             goexe = ".exe";
219         }
220         env.insert("GOEXE",goexe);
221     }
222 
223     QString goarch = env.value("GOARCH");
224     QString goroot = env.value("GOROOT");
225     if (goroot.isEmpty()) {
226         goroot = getDefaultGOROOT();
227     }
228 
229     if (app->settings()->value("liteide/use111gomodule",false).toBool()) {
230         env.insert("GO111MODULE",app->settings()->value("liteide/go111module").toString());
231     }
232     if (app->settings()->value("liteide/usegoproxy",false).toBool()) {
233         env.insert("GOPROXY",app->settings()->value("liteide/goproxy").toString());
234     }
235     if (app->settings()->value("liteide/usegoprivate",false).toBool()) {
236         env.insert("GOPRIVATE",app->settings()->value("liteide/goprivate").toString());
237     }
238     if (app->settings()->value("liteide/usegonoproxy",false).toBool()) {
239         env.insert("GONOPROXY",app->settings()->value("liteide/gonoproxy").toString());
240     }
241     if (app->settings()->value("liteide/usegonosumdb",false).toBool()) {
242         env.insert("GONOSUMDB",app->settings()->value("liteide/gonosumdb").toString());
243     }
244 
245     QStringList pathList;
246     if (app->settings()->value("liteide/usesysgopath",true).toBool()) {
247         foreach (QString path, env.value("GOPATH").split(sep,QString::SkipEmptyParts)) {
248             pathList.append(QDir::toNativeSeparators(path));
249         }
250     }
251     if (app->settings()->value("liteide/uselitegopath",true).toBool()) {
252         foreach (QString path, app->settings()->value("liteide/gopath").toStringList()) {
253             pathList.append(QDir::toNativeSeparators(path));
254         }
255     }
256     pathList.removeDuplicates();
257     env.insert("GOPATH",pathList.join(sep));
258 
259     if (!goroot.isEmpty()) {
260         pathList.prepend(goroot);
261     }
262 
263     QStringList binList;
264     QString gobin = env.value("GOBIN");
265     if (!gobin.isEmpty()) {
266         binList.append(gobin);
267     }
268     foreach (QString path, pathList) {
269         binList.append(QFileInfo(path,"bin").filePath());
270         binList.append(QFileInfo(path,"bin/"+goos+"_"+goarch).filePath());
271     }
272     env.insert("PATH",env.value("PATH")+sep+binList.join(sep)+sep);
273 
274     return env;
275 }
276 
getGOPATH(LiteApi::IApplication * app,bool includeGoroot)277 inline QStringList getGOPATH(LiteApi::IApplication *app, bool includeGoroot)
278 {
279     QProcessEnvironment env = getGoEnvironment(app);
280 #ifdef Q_OS_WIN
281     QString sep = ";";
282 #else
283     QString sep = ":";
284 #endif
285     QStringList pathList;
286     QString goroot = QDir::toNativeSeparators(env.value("GOROOT"));
287     if (includeGoroot) {
288         pathList.append(goroot);
289     }
290     foreach (QString path, env.value("GOPATH").split(sep,QString::SkipEmptyParts)) {
291         pathList.append(QDir::toNativeSeparators(path));
292     }
293     if (!includeGoroot) {
294         pathList.removeAll(goroot);
295     }
296     pathList.removeDuplicates();
297     return pathList;
298 }
299 
getGOROOT(LiteApi::IApplication * app)300 inline QString getGOROOT(LiteApi::IApplication *app)
301 {
302     return getGoEnvironment(app).value("GOROOT");
303 }
304 
lookupSrcRoot(const QString & buildFilePath)305 inline QString lookupSrcRoot(const QString &buildFilePath)
306 {
307     int index = buildFilePath.indexOf("/src/");
308     if (index < 0) {
309         return QString();
310     }
311     return buildFilePath.left(index+4);
312 }
313 
314 inline QString lookupParentHasCustom(LiteApi::IApplication *app, const QString &buildFilePath, const QString &srcRoot, QString *pCustomParent = 0)
315 {
316     QFileInfo info(buildFilePath);
317     QString parent = info.path();
318 
319     if (parent == srcRoot || info.dir().isRoot()) {
320         return QString();
321     }
322     QString customKey = "litebuild-custom/"+parent;
323     bool use_custom_gopath = app->settings()->value(customKey+"#use_custom_gopath",false).toBool();
324     if (use_custom_gopath) {
325         if (pCustomParent) {
326             *pCustomParent = parent;
327         }
328         return customKey;
329     }
330     return lookupParentHasCustom(app,parent,srcRoot);
331 }
332 
333 inline QProcessEnvironment getCustomGoEnvironment(LiteApi::IApplication *app, const QString &buildFilePath, QString *pCustomBuildPath = 0)
334 {
335     if (buildFilePath.isEmpty()) {
336         return getGoEnvironment(app);
337     }
338     QString customKey = "litebuild-custom/"+buildFilePath;
339     QString customBuildPath = buildFilePath;
340     bool use_custom_gopath = app->settings()->value(customKey+"#use_custom_gopath",false).toBool();
341     if (!use_custom_gopath) {
342         QString srcRoot = lookupSrcRoot(buildFilePath);
343         if (!srcRoot.isEmpty()) {
344             customKey = lookupParentHasCustom(app,buildFilePath,srcRoot, &customBuildPath);
345             if (!customKey.isEmpty()) {
346                 use_custom_gopath = true;
347             }
348         }
349     }
350     if (!use_custom_gopath) {
351         return getGoEnvironment(app);
352     }
353     if (pCustomBuildPath) {
354         *pCustomBuildPath = customBuildPath;
355     }
356 
357     QProcessEnvironment env = getCurrentEnvironment(app);
358 #ifdef Q_OS_WIN
359     QString sep = ";";
360 #else
361     QString sep = ":";
362 #endif
363 
364     IEnvManager *mgr = LiteApi::getEnvManager(app);
365     if (mgr) {
366         LiteApi::IEnv *ce = mgr->currentEnv();
367         if (ce) {
368             QMapIterator<QString,QString> i(ce->goEnvMap());
369             while(i.hasNext()) {
370                 i.next();
371                 env.insert(i.key(),i.value());
372             }
373         }
374     }
375 
376     QString goos = env.value("GOOS");
377     if (goos.isEmpty()) {
378         goos = getDefaultGOOS();
379     }
380     if (!env.contains("GOEXE")) {
381         QString goexe;
382         if (goos == "windows") {
383             goexe = ".exe";
384         }
385         env.insert("GOEXE",goexe);
386     }
387 
388     QString goarch = env.value("GOARCH");
389     QString goroot = env.value("GOROOT");
390     if (goroot.isEmpty()) {
391         goroot = getDefaultGOROOT();
392     }
393 
394     QStringList pathList;
395 
396     bool inherit_sys_gopath = app->settings()->value(customKey+"#inherit_sys_gopath",true).toBool();
397     bool inherit_lite_gopath = app->settings()->value(customKey+"#inherit_lite_gopath",true).toBool();
398     bool custom_gopath = app->settings()->value(customKey+"#custom_gopath",false).toBool();
399 
400     if (inherit_sys_gopath) {
401         foreach (QString path, env.value("GOPATH").split(sep,QString::SkipEmptyParts)) {
402             pathList.append(QDir::toNativeSeparators(path));
403         }
404     }
405     if (inherit_lite_gopath) {
406         foreach (QString path, app->settings()->value("liteide/gopath").toStringList()) {
407             pathList.append(QDir::toNativeSeparators(path));
408         }
409     }
410     if (custom_gopath) {
411         foreach (QString path, app->settings()->value(customKey+"#gopath").toStringList()) {
412             pathList.append(QDir::toNativeSeparators(path));
413         }
414     }
415     pathList.removeDuplicates();
416     env.insert("GOPATH",pathList.join(sep));
417 
418     if (!goroot.isEmpty()) {
419         pathList.prepend(goroot);
420     }
421 
422     QStringList binList;
423     QString gobin = env.value("GOBIN");
424     if (!gobin.isEmpty()) {
425         binList.append(gobin);
426     }
foreach(QString path,pathList)427     foreach (QString path, pathList) {
428         binList.append(QFileInfo(path,"bin").filePath());
429         binList.append(QFileInfo(path,"bin/"+goos+"_"+goarch).filePath());
430     }
431     env.insert("PATH",env.value("PATH")+sep+binList.join(sep)+sep);
432     return env;
433 }
434 
getCustomGoEnvironment(LiteApi::IApplication * app,LiteApi::IEditor * editor)435 inline QProcessEnvironment getCustomGoEnvironment(LiteApi::IApplication *app, LiteApi::IEditor *editor)
436 {
437     QString buildFilePath;
438     if (editor) {
439         QString filePath = editor->filePath();
440         if (!filePath.isEmpty()) {
441             buildFilePath = QFileInfo(filePath).path();
442         }
443     }
444     return getCustomGoEnvironment(app,buildFilePath);
445 }
446 
447 } //namespace LiteApi
448 
449 
450 #endif //LITEENVAPI_H
451 
452