Skip to content

Commit 048f665

Browse files
authored
System frameworks / libraries for macOS, iOS, tvOS kept in config.make, GUI fields and help tab (#690)
* System frameworks / libraries for macOS, iOS, tvOS kept in config.make, GUI fields and help tab * Updated cmdLine from 0.117.0 to 0.118.0 and PG GUI from 0.111.0 to 0.112.0 * System frameworks / libraries fields in advanced options * System frameworks / libraries can be separated by spaces too
1 parent cf0cf4d commit 048f665

21 files changed

Lines changed: 343 additions & 14 deletions

‎commandLine/readme.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ here's the extended options if you want to experiment. please take are that opt
2626
-v, --verbose run verbose
2727
-t"project_template", --template="project_template" project template
2828
-d, --dryrun don't change files
29+
-f"frameworks", --frameworks="frameworks" Apple system frameworks
30+
(Security,CoreBluetooth)
31+
--system-libs="libraries" Apple system libraries
32+
(sqlite3,z)
33+
34+
run with --help for every option.
2935

3036

3137
**note that on windows, command line flags are written with "/" not "-" and don't have abbreviations, so -r would be /recursive**
@@ -60,6 +66,20 @@ you can also specify platforms (default platform is inferred) such as:
6066
./projectGenerator -o"pathToOF" -p"ios" pathOfNewIOSProject
6167

6268

69+
## System frameworks and libraries (macOS / iOS / tvOS)
70+
71+
Link SDK frameworks and libraries (sqlite3 = `libsqlite3.tbd`) to a project:
72+
73+
./projectGenerator -o"pathToOF" -p"osx" -f"Security,CoreBluetooth" --system-libs="sqlite3,z" pathToProject
74+
75+
`libsqlite3`, `libsqlite3.tbd`, `-lsqlite3` and `Security.framework` work too. They are saved with the project in `config.make`:
76+
77+
PROJECT_FRAMEWORKS = Security CoreBluetooth
78+
PROJECT_SYSTEM_LIBS = sqlite3 z
79+
80+
and read back on every update, so a plain `./projectGenerator -o"pathToOF" pathToProject` keeps them. The Xcode project gets them in Link Binary With Libraries; the makefiles and CMake read the same lines (`-framework Security` on Apple only, `-lsqlite3` everywhere). Edit `config.make` and update the project, or pass an empty `-f` / `--system-libs=` to remove them.
81+
82+
The GUI shows them as System frameworks / System libraries with Advanced options on and an Apple platform selected (see the help tab).
6383

6484

6585

‎commandLine/src/defines.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#define OFPROJECTGENERATOR_MAJOR_VERSION "0"
2-
#define OFPROJECTGENERATOR_MINOR_VERSION "117"
2+
#define OFPROJECTGENERATOR_MINOR_VERSION "118"
33
#define OFPROJECTGENERATOR_PATCH_VERSION "0"
44

55
#define PG_VERSION (OFPROJECTGENERATOR_MAJOR_VERSION "." OFPROJECTGENERATOR_MINOR_VERSION "." OFPROJECTGENERATOR_PATCH_VERSION)

‎commandLine/src/main.cpp‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum optionIndex { UNKNOWN,
3232
COMMAND,
3333
BACKUP_PROJECT_FILES,
3434
FRAMEWORKS,
35+
SYSTEMLIBS,
3536
CLEANNAME_DISABLE,
3637
DEFINES,
3738
POSTBUILD,
@@ -61,7 +62,8 @@ constexpr option::Descriptor usage[] = {
6162
{ COMMAND, 0, "c", "command", option::Arg::None, " --command, -c \truns command" },
6263
{ BACKUP_PROJECT_FILES, 0, "b", "backup", option::Arg::None, " --backup, -b \tbackup project files when replacing with template" },
6364

64-
{ FRAMEWORKS, 0, "f", "frameworks", option::Arg::Optional, " --frameworks, -f \tframeworks list (such as Vision,ARKit)" },
65+
{ FRAMEWORKS, 0, "f", "frameworks", option::Arg::Optional, " --frameworks, -f \tApple system frameworks (such as Security,CoreBluetooth), saved in config.make PROJECT_FRAMEWORKS. Empty removes them" },
66+
{ SYSTEMLIBS, 0, "", "system-libs", option::Arg::Optional, " --system-libs \tApple system libraries (such as sqlite3,z = libsqlite3.tbd, libz.tbd), saved in config.make PROJECT_SYSTEM_LIBS. Empty removes them" },
6567

6668
{ CLEANNAME_DISABLE, 0, "n", "cleanname", option::Arg::Optional, " --cleanname, -f \tcleanname" },
6769

@@ -98,6 +100,9 @@ vector<string> addons;
98100
vector<fs::path> srcPaths;
99101
vector<string> targets;
100102
vector<string> frameworks;
103+
bool bFrameworksPassedIn = false;
104+
vector<string> systemLibs;
105+
bool bSystemLibsPassedIn = false;
101106
vector<string> defines;
102107
string postBuildScript;
103108
string emscriptenWasm; // empty keeps the project's config.make value
@@ -276,6 +281,7 @@ void updateProject(const fs::path & path, const string & target, bool bConsiderP
276281
project->setMsys2Environment(msys2Env);
277282
project->setEmscriptenWasm(emscriptenWasm);
278283
project->setAppVersion(appVersion, appBuild);
284+
project->setSystemFrameworks(frameworks, bFrameworksPassedIn, systemLibs, bSystemLibsPassedIn);
279285
project->create(path, templateName);
280286

281287
if (bConsiderParameterAddons && bAddonsPassedIn) {
@@ -286,9 +292,7 @@ void updateProject(const fs::path & path, const string & target, bool bConsiderP
286292
ofLogNotice() << "parsing addons.make";
287293
project->parseAddons();
288294
}
289-
for (auto & f : frameworks) {
290-
project->addFramework(f, "Frameworks", true);
291-
}
295+
project->addSystemFrameworks();
292296

293297
// all build configs (visualStudioProject overrides addProjectDefine for Debug + Release)
294298
for (auto & d : defines) {
@@ -624,10 +628,16 @@ int pgMain(int argc, char ** argv) {
624628
#endif
625629

626630
if (options[FRAMEWORKS].count() > 0) {
627-
bAddonsPassedIn = true; // could be empty
631+
bFrameworksPassedIn = true; // could be empty
628632
if (options[FRAMEWORKS].arg != NULL) {
629633
frameworks = ofSplitString(options[FRAMEWORKS].arg, ",", true, true);
630-
// cout << "frameworks " << options[FRAMEWORKS].arg << endl;
634+
}
635+
}
636+
637+
if (options[SYSTEMLIBS].count() > 0) {
638+
bSystemLibsPassedIn = true; // could be empty
639+
if (options[SYSTEMLIBS].arg != NULL) {
640+
systemLibs = ofSplitString(options[SYSTEMLIBS].arg, ",", true, true);
631641
}
632642
}
633643

@@ -807,6 +817,7 @@ int pgMain(int argc, char ** argv) {
807817
project->setMsys2Environment(msys2Env);
808818
project->setEmscriptenWasm(emscriptenWasm);
809819
project->setAppVersion(appVersion, appBuild);
820+
project->setSystemFrameworks(frameworks, bFrameworksPassedIn, systemLibs, bSystemLibsPassedIn);
810821
project->create(projectPath, templateName);
811822
if(bAddonsPassedIn){
812823
for (auto & addon : addons) {
@@ -815,9 +826,7 @@ int pgMain(int argc, char ** argv) {
815826
}else{
816827
project->parseAddons();
817828
}
818-
for (auto & f : frameworks) {
819-
project->addFramework(f, "Frameworks", true);
820-
}
829+
project->addSystemFrameworks();
821830

822831
for (auto & d : defines) {
823832
project->addProjectDefine(d);

‎commandLine/src/projects/baseProject.cpp‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,51 @@ static std::string readEmscriptenWasm(const fs::path & configMake) {
129129
return bits;
130130
}
131131

132+
// space separated list from config.make, VAR = a b / VAR += c
133+
static std::vector<std::string> readMakeList(const fs::path & configMake, const std::string & name) {
134+
std::vector<std::string> values;
135+
if (!fs::exists(configMake)) return values;
136+
for (auto & line : fileToStrings(configMake)) {
137+
auto config = ofTrim(line);
138+
if (config.empty() || config[0] == '#') continue;
139+
auto eq = config.find('=');
140+
if (eq == std::string::npos) continue;
141+
auto var = ofTrim(config.substr(0, eq));
142+
bool append = !var.empty() && var.back() == '+';
143+
if (!var.empty() && (var.back() == '?' || var.back() == '+' || var.back() == ':')) var = ofTrim(var.substr(0, var.size() - 1));
144+
if (var != name) continue;
145+
if (!append) values.clear();
146+
for (auto & v : ofSplitString(ofTrim(config.substr(eq + 1)), " ", true, true)) values.emplace_back(v);
147+
}
148+
return values;
149+
}
150+
151+
// Security.framework -> Security
152+
static std::string cleanFrameworkName(std::string name) {
153+
name = ofTrim(name);
154+
if (name.size() > 10 && name.substr(name.size() - 10) == ".framework") name = name.substr(0, name.size() - 10);
155+
return name;
156+
}
157+
158+
// -lsqlite3 / libsqlite3 / libsqlite3.tbd / libsqlite3.dylib -> sqlite3
159+
static std::string cleanSystemLibName(std::string name) {
160+
name = ofTrim(name);
161+
if (name.rfind("-l", 0) == 0) name = name.substr(2);
162+
for (std::string ext : { ".tbd", ".dylib" }) {
163+
if (name.size() > ext.size() && name.substr(name.size() - ext.size()) == ext) name = name.substr(0, name.size() - ext.size());
164+
}
165+
if (name.rfind("lib", 0) == 0 && name.size() > 3) name = name.substr(3);
166+
return name;
167+
}
168+
169+
static bool isSystemLibName(const std::string & name) {
170+
return name.rfind("-l", 0) == 0 || name.rfind("lib", 0) == 0 || ofIsStringInString(name, ".tbd") || ofIsStringInString(name, ".dylib");
171+
}
172+
173+
static void addUnique(std::vector<std::string> & list, const std::string & value) {
174+
if (!value.empty() && std::find(list.begin(), list.end(), value) == list.end()) list.emplace_back(value);
175+
}
176+
132177
static const std::string OF_CMAKE_INCLUDE { "libs/openFrameworksCompiled/project/CMake/openFrameworks.cmake" };
133178

134179
// set(PROJECT_EMSCRIPTEN_WASM 64) written by PG into an openFrameworks CMakeLists.txt (openFrameworks#8586)
@@ -252,6 +297,18 @@ bool baseProject::create(const fs::path & _path, string templateName){
252297
ofLogNotice() << "{ \"emscriptenWasm\": \"" << emscriptenWasm << "\" }";
253298
}
254299

300+
systemFrameworks.clear();
301+
systemLibs.clear();
302+
for (auto & f : systemFrameworksPassed ? systemFrameworksOption : readMakeList(projectDir / "config.make", "PROJECT_FRAMEWORKS")) {
303+
if (isSystemLibName(f)) addUnique(systemLibs, cleanSystemLibName(f));
304+
else addUnique(systemFrameworks, cleanFrameworkName(f));
305+
}
306+
for (auto & l : systemLibsPassed ? systemLibsOption : readMakeList(projectDir / "config.make", "PROJECT_SYSTEM_LIBS")) {
307+
addUnique(systemLibs, cleanSystemLibName(l));
308+
}
309+
if (systemFrameworks.size()) ofLogNotice() << "{ \"systemFrameworks\": \"" << ofJoinString(systemFrameworks, " ") << "\" }";
310+
if (systemLibs.size()) ofLogNotice() << "{ \"systemLibs\": \"" << ofJoinString(systemLibs, " ") << "\" }";
311+
255312
// app version / build: config.make first, then CMakeLists, Xcode, Android; the CLI value wins
256313
{
257314
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> found {
@@ -427,6 +484,28 @@ bool baseProject::save(){
427484
bool bWroteWasm = false;
428485
bool bWroteAppVersion = false;
429486
bool bWroteAppBuild = false;
487+
bool bWroteFrameworks = false;
488+
bool bWroteSystemLibs = false;
489+
auto isMakeVar = [](const std::string & line, const std::string & name) {
490+
auto config = ofTrim(line);
491+
if (config.rfind("#", 0) == 0) config = ofTrim(config.substr(1));
492+
if (config.rfind(name, 0) != 0) return false;
493+
auto rest = ofTrim(config.substr(name.size()));
494+
return rest.rfind("=", 0) == 0 || rest.rfind("+=", 0) == 0 || rest.rfind(":=", 0) == 0 || rest.rfind("?=", 0) == 0;
495+
};
496+
// the list on the first line, commented when empty, later += lines are folded in
497+
auto writeMakeList = [&](const std::string & line, const std::string & name, const std::vector<std::string> & values, bool & bWrote) {
498+
if (bWrote) {
499+
if (ofTrim(line).rfind("#", 0) == 0) saveConfig << line << std::endl;
500+
return;
501+
}
502+
if (values.empty()) {
503+
saveConfig << (ofTrim(line).rfind("#", 0) == 0 ? line : "# " + name + " =") << std::endl;
504+
} else {
505+
saveConfig << name << " = " << ofJoinString(values, " ") << std::endl;
506+
}
507+
bWrote = true;
508+
};
430509

431510
for (auto & str : lines) {
432511
//add the of root path
@@ -472,6 +551,12 @@ bool baseProject::save(){
472551
bWroteAppBuild = true;
473552
}
474553
}
554+
else if( isMakeVar(str, "PROJECT_FRAMEWORKS") ){
555+
writeMakeList(str, "PROJECT_FRAMEWORKS", systemFrameworks, bWroteFrameworks);
556+
}
557+
else if( isMakeVar(str, "PROJECT_SYSTEM_LIBS") ){
558+
writeMakeList(str, "PROJECT_SYSTEM_LIBS", systemLibs, bWroteSystemLibs);
559+
}
475560
else if( emscriptenWasm == "32" && str.rfind("EMSCRIPTEN_MEMORY64", 0) == 0 ){
476561
saveConfig << "# " << str << std::endl;
477562
} else {
@@ -490,6 +575,12 @@ bool baseProject::save(){
490575
if (!bWroteAppVersion) saveConfig << "APP_VERSION = " << appVersion << std::endl;
491576
if (!bWroteAppBuild) saveConfig << "APP_BUILD = " << appBuild << std::endl;
492577
}
578+
if ((systemFrameworks.size() && !bWroteFrameworks) || (systemLibs.size() && !bWroteSystemLibs)) {
579+
if (lines.size()) saveConfig << std::endl;
580+
saveConfig << "# system frameworks (Security) and libraries (sqlite3 = libsqlite3.tbd / -lsqlite3)" << std::endl;
581+
if (systemFrameworks.size() && !bWroteFrameworks) saveConfig << "PROJECT_FRAMEWORKS = " << ofJoinString(systemFrameworks, " ") << std::endl;
582+
if (systemLibs.size() && !bWroteSystemLibs) saveConfig << "PROJECT_SYSTEM_LIBS = " << ofJoinString(systemLibs, " ") << std::endl;
583+
}
493584
saveConfig.close();
494585
saveCMakeLists();
495586
saveAppVersionFiles();
@@ -1010,6 +1101,30 @@ void baseProject::parseConfigMake(){
10101101
}
10111102
}
10121103

1104+
void baseProject::setSystemFrameworks(const std::vector<std::string> & frameworks, bool frameworksPassed, const std::vector<std::string> & libs, bool libsPassed) {
1105+
// "Security CoreBluetooth" is two, names have no spaces
1106+
auto splitSpaces = [](const std::vector<std::string> & list) {
1107+
std::vector<std::string> out;
1108+
for (auto & l : list) {
1109+
for (auto & v : ofSplitString(l, " ", true, true)) out.emplace_back(v);
1110+
}
1111+
return out;
1112+
};
1113+
systemFrameworksOption = splitSpaces(frameworks);
1114+
systemFrameworksPassed = frameworksPassed;
1115+
systemLibsOption = splitSpaces(libs);
1116+
systemLibsPassed = libsPassed;
1117+
}
1118+
1119+
void baseProject::addSystemFrameworks() {
1120+
for (auto & f : systemFrameworks) {
1121+
addFramework(f, "Frameworks", true);
1122+
}
1123+
for (auto & l : systemLibs) {
1124+
addSystemLibrary(l);
1125+
}
1126+
}
1127+
10131128
void baseProject::recursiveTemplateCopy(const fs::path & srcDir, const fs::path & destDir){
10141129
if (recursiveCopy(srcDir, destDir)) {
10151130
fs::path templateFile = destDir / "template.config";

‎commandLine/src/projects/baseProject.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ class baseProject {
9797
bool bOverwrite = true;
9898

9999
virtual void addFramework(const fs::path & path, const fs::path & folder, bool isRelativeToSDK = false){};
100+
// SDK library like sqlite3 = usr/lib/libsqlite3.tbd (Xcode only)
101+
virtual void addSystemLibrary(const std::string & name){};
102+
103+
// system frameworks / libraries (-f / --system-libs), kept in config.make PROJECT_FRAMEWORKS / PROJECT_SYSTEM_LIBS
104+
void setSystemFrameworks(const std::vector<std::string> & frameworks, bool frameworksPassed, const std::vector<std::string> & libs, bool libsPassed);
105+
void addSystemFrameworks();
100106

101107
// public entry point for project-level preprocessor defines (e.g. -D/--defines on the
102108
// CLI, or a GUI-driven renderer choice) - addDefine() itself stays protected since it's
@@ -185,6 +191,9 @@ class baseProject {
185191
std::vector<fs::path> extSrcPaths;
186192
std::vector<std::string> projectDefines; // also written to config.make PROJECT_DEFINES for make builds
187193
std::vector<std::string> missingAddons; // not found, still written to addons.make (#258)
194+
std::vector<std::string> systemFrameworks, systemLibs; // value for this project
195+
std::vector<std::string> systemFrameworksOption, systemLibsOption; // from the CLI
196+
bool systemFrameworksPassed = false, systemLibsPassed = false; // false = keep the project's
188197
std::string emscriptenWasmOption; // from the CLI, empty = keep
189198
std::string emscriptenWasm; // value for this project: "", "32" or "64"
190199
void saveCMakeLists();

‎commandLine/src/projects/xcodeProject.cpp‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,17 @@ void xcodeProject::addFramework(const fs::path & path, const fs::path & folder,
666666
}
667667
}
668668

669+
void xcodeProject::addSystemLibrary(const std::string & name) {
670+
ofLogVerbose() << "Adding system library " << name;
671+
addCommand("# ----- addSystemLibrary " + name);
672+
673+
fileProperties fp;
674+
fp.absolute = false;
675+
fp.isRelativeToSDK = true;
676+
fp.linkBinaryWithLibraries = true;
677+
addFile(fs::path { "usr/lib/lib" + name + ".tbd" }, "Frameworks", fp);
678+
}
679+
669680
void xcodeProject::addDylib(const fs::path & path, const fs::path & folder) {
670681
// alert( "xcodeProject::addDylib " + ofPathToString(path) , 33);
671682

‎commandLine/src/projects/xcodeProject.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class xcodeProject : public baseProject {
5555

5656
void addCompileFlagsForMMFile(const fs::path & srcFile);
5757
void addFramework(const fs::path & path, const fs::path & folder, bool isRelativeToSDK = false) override;
58+
void addSystemLibrary(const std::string & name) override;
5859
void addXCFramework(const fs::path & path, const fs::path & folder);
5960
void addDylib(const fs::path & path, const fs::path & folder);
6061

@@ -125,6 +126,7 @@ class xcodeProject : public baseProject {
125126
{ ".framework" , "wrapper.framework" },
126127
{ ".xcframework" , "wrapper.xcframework" },
127128
{ ".dylib" , "compiled.mach-o.dylib" },
129+
{ ".tbd" , "sourcecode.text-based-dylib-definition" },
128130

129131
{ ".cpp" , "sourcecode.cpp.cpp" },
130132
{ ".c" , "sourcecode.cpp.c" },

‎frontend/app.js‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ ipcRenderer.on('setGenerateMode', (event, arg) => {
218218
$('#appVersion').val('');
219219
$('#appBuild').val('');
220220
$('#appVersionNote').hide();
221+
$('#systemFrameworks').val('');
222+
$('#systemLibs').val('');
221223
}
222224
});
223225

@@ -230,6 +232,13 @@ ipcRenderer.on('setAppVersion', (event, { version, build, differs }) => {
230232
$('#appVersionNote').text(note).toggle(!!defaultSettings.advancedMode && note.length > 0);
231233
});
232234

235+
//-------------------------------------------
236+
// existing project's config.make PROJECT_FRAMEWORKS / PROJECT_SYSTEM_LIBS, so updating keeps them
237+
ipcRenderer.on('setSystemFrameworks', (event, { frameworks, libs }) => {
238+
$('#systemFrameworks').val((frameworks || []).join(','));
239+
$('#systemLibs').val((libs || []).join(','));
240+
});
241+
233242
//-------------------------------------------
234243
// existing project's PROJECT_EMSCRIPTEN_WASM, so updating keeps it
235244
ipcRenderer.on('setEmscriptenWasm', (event, bits) => {
@@ -1276,7 +1285,10 @@ function generate() {
12761285
msys2Env: $('#msys2EnvField').css('display') !== 'none' ? $('#msys2EnvDropdown').dropdown('get value') : '',
12771286
emscriptenWasm: $('#wasmField').css('display') !== 'none' ? $('#wasmDropdown').dropdown('get value') : '',
12781287
appVersion: $('#appVersion').val().trim(),
1279-
appBuild: $('#appBuild').val().trim()
1288+
appBuild: $('#appBuild').val().trim(),
1289+
// only for Apple targets; hidden = keep the project's config.make values
1290+
systemFrameworks: $('#systemFrameworksField').css('display') !== 'none' ? $('#systemFrameworks').val().split(/[\s,]+/).filter(Boolean) : null,
1291+
systemLibs: $('#systemFrameworksField').css('display') !== 'none' ? $('#systemLibs').val().split(/[\s,]+/).filter(Boolean) : null
12801292
};
12811293

12821294
// console.log(gen);
@@ -1469,6 +1481,7 @@ function updateRendererFieldVisibility() {
14691481
// advanced only; hidden fields keep the project's values, so updating still keeps them
14701482
$('#appVersionField').css('display', defaultSettings.advancedMode ? 'flex' : 'none');
14711483
$('#appVersionNote').toggle(!!defaultSettings.advancedMode && $('#appVersionNote').text().length > 0);
1484+
$('#systemFrameworksField').css('display', defaultSettings.advancedMode && selectedPlatforms.some((p) => ['osx', 'ios', 'macos', 'tvos'].includes(p)) ? 'flex' : 'none');
14721485

14731486
const glPlatforms = ['osx', 'vs', 'msys2', 'linux', 'linux64', 'linuxarmv6l', 'linuxarmv7l', 'linuxaarch64', 'vscode'];
14741487
$('#glField').toggle(!!defaultSettings.advancedMode && selectedPlatforms.some((p) => glPlatforms.includes(p)));

0 commit comments

Comments
 (0)