Skip to content

Commit 44c26be

Browse files
committed
Swift: make dealing with CF types typesafe
1 parent d97669f commit 44c26be

File tree

4 files changed

+93
-56
lines changed

4 files changed

+93
-56
lines changed

swift/xcode-autobuilder/CFHelpers.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include "swift/xcode-autobuilder/CFHelpers.h"
2+
3+
#include <iostream>
4+
5+
typedef CFTypeID (*cf_get_type_id)();
6+
7+
template <typename CFType, cf_get_type_id get_type_id>
8+
CFType cf_cast(const void* ptr) {
9+
if (!ptr) {
10+
return nullptr;
11+
}
12+
if (CFGetTypeID(ptr) != get_type_id()) {
13+
std::cerr << "Unexpected type: ";
14+
CFShow(ptr);
15+
abort();
16+
}
17+
return static_cast<CFType>(ptr);
18+
}
19+
20+
CFStringRef cf_string_ref(const void* ptr) {
21+
return cf_cast<CFStringRef, CFStringGetTypeID>(ptr);
22+
}
23+
24+
CFArrayRef cf_array_ref(const void* ptr) {
25+
return cf_cast<CFArrayRef, CFArrayGetTypeID>(ptr);
26+
}
27+
CFDictionaryRef cf_dictionary_ref(const void* ptr) {
28+
return cf_cast<CFDictionaryRef, CFDictionaryGetTypeID>(ptr);
29+
}
30+
31+
std::string stringValueForKey(CFDictionaryRef dict, CFStringRef key) {
32+
auto cfValue = cf_string_ref(CFDictionaryGetValue(dict, key));
33+
if (cfValue) {
34+
const int bufferSize = 256;
35+
char buf[bufferSize];
36+
if (CFStringGetCString(cfValue, buf, bufferSize, kCFStringEncodingUTF8)) {
37+
return {buf};
38+
}
39+
}
40+
return {};
41+
}

swift/xcode-autobuilder/CFHelpers.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <CoreFoundation/CoreFoundation.h>
4+
#include <string>
5+
#include <vector>
6+
7+
CFStringRef cf_string_ref(const void* ptr);
8+
CFArrayRef cf_array_ref(const void* ptr);
9+
CFDictionaryRef cf_dictionary_ref(const void* ptr);
10+
11+
std::string stringValueForKey(CFDictionaryRef dict, CFStringRef key);
12+
13+
struct CFKeyValues {
14+
static CFKeyValues fromDictionary(CFDictionaryRef dict) {
15+
auto size = CFDictionaryGetCount(dict);
16+
CFKeyValues ret(size);
17+
CFDictionaryGetKeysAndValues(dict, ret.keys.data(), ret.values.data());
18+
return ret;
19+
}
20+
explicit CFKeyValues(size_t size) : size(size), keys(size), values(size) {}
21+
size_t size;
22+
std::vector<const void*> keys;
23+
std::vector<const void*> values;
24+
};

swift/xcode-autobuilder/XcodeProjectParser.cpp

Lines changed: 27 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "swift/xcode-autobuilder/XcodeProjectParser.h"
22
#include "swift/xcode-autobuilder/XcodeWorkspaceParser.h"
3+
#include "swift/xcode-autobuilder/CFHelpers.h"
34

45
#include <iostream>
56
#include <filesystem>
@@ -16,31 +17,6 @@ struct TargetData {
1617
std::string type;
1718
};
1819

19-
struct CFKeyValues {
20-
static CFKeyValues fromDictionary(CFDictionaryRef dict) {
21-
auto size = CFDictionaryGetCount(dict);
22-
CFKeyValues ret(size);
23-
CFDictionaryGetKeysAndValues(dict, ret.keys.data(), ret.values.data());
24-
return ret;
25-
}
26-
explicit CFKeyValues(size_t size) : size(size), keys(size), values(size) {}
27-
size_t size;
28-
std::vector<const void*> keys;
29-
std::vector<const void*> values;
30-
};
31-
32-
static std::string stringValue(CFDictionaryRef dict, CFStringRef key) {
33-
auto cfValue = (CFStringRef)CFDictionaryGetValue(dict, key);
34-
if (cfValue) {
35-
const int bufferSize = 256;
36-
char buf[bufferSize];
37-
if (CFStringGetCString(cfValue, buf, bufferSize, kCFStringEncodingUTF8)) {
38-
return {buf};
39-
}
40-
}
41-
return {};
42-
}
43-
4420
typedef std::unordered_map<std::string, CFDictionaryRef> Targets;
4521
typedef std::unordered_map<std::string, std::vector<std::string>> Dependencies;
4622
typedef std::unordered_map<std::string, std::vector<std::pair<std::string, CFDictionaryRef>>>
@@ -57,7 +33,7 @@ static size_t totalFilesCount(const std::string& target,
5733
}
5834

5935
static bool objectIsTarget(CFDictionaryRef object) {
60-
auto isa = (CFStringRef)CFDictionaryGetValue(object, CFSTR("isa"));
36+
auto isa = cf_string_ref(CFDictionaryGetValue(object, CFSTR("isa")));
6137
if (isa) {
6238
for (auto target :
6339
{CFSTR("PBXAggregateTarget"), CFSTR("PBXNativeTarget"), CFSTR("PBXLegacyTarget")}) {
@@ -77,66 +53,66 @@ static void mapTargetsToSourceFiles(CFDictionaryRef objects,
7753

7854
auto kv = CFKeyValues::fromDictionary(objects);
7955
for (size_t i = 0; i < kv.size; i++) {
80-
auto object = (CFDictionaryRef)kv.values[i];
56+
auto object = cf_dictionary_ref(kv.values[i]);
8157
if (objectIsTarget(object)) {
82-
auto name = stringValue(object, CFSTR("name"));
58+
auto name = stringValueForKey(object, CFSTR("name"));
8359
dependencies[name] = {};
8460
buildFiles[name] = {};
8561
targets.emplace(name, object);
8662
}
8763
}
8864

8965
for (auto& [targetName, targetObject] : targets) {
90-
auto deps = (CFArrayRef)CFDictionaryGetValue(targetObject, CFSTR("dependencies"));
66+
auto deps = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("dependencies")));
9167
auto size = CFArrayGetCount(deps);
9268
for (CFIndex i = 0; i < size; i++) {
93-
auto dependencyID = (CFStringRef)CFArrayGetValueAtIndex(deps, i);
94-
auto dependency = (CFDictionaryRef)CFDictionaryGetValue(objects, dependencyID);
95-
auto targetID = (CFStringRef)CFDictionaryGetValue(dependency, CFSTR("target"));
69+
auto dependencyID = cf_string_ref(CFArrayGetValueAtIndex(deps, i));
70+
auto dependency = cf_dictionary_ref(CFDictionaryGetValue(objects, dependencyID));
71+
auto targetID = cf_string_ref(CFDictionaryGetValue(dependency, CFSTR("target")));
9672
if (!targetID) {
9773
// Skipping non-targets (e.g., productRef)
9874
continue;
9975
}
100-
auto targetDependency = (CFDictionaryRef)CFDictionaryGetValue(objects, targetID);
101-
auto dependencyName = stringValue(targetDependency, CFSTR("name"));
76+
auto targetDependency = cf_dictionary_ref(CFDictionaryGetValue(objects, targetID));
77+
auto dependencyName = stringValueForKey(targetDependency, CFSTR("name"));
10278
if (!dependencyName.empty()) {
10379
dependencies[targetName].push_back(dependencyName);
10480
}
10581
}
10682
}
10783

10884
for (auto& [targetName, targetObject] : targets) {
109-
auto buildPhases = (CFArrayRef)CFDictionaryGetValue(targetObject, CFSTR("buildPhases"));
85+
auto buildPhases = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("buildPhases")));
11086
auto buildPhaseCount = CFArrayGetCount(buildPhases);
11187
for (CFIndex buildPhaseIndex = 0; buildPhaseIndex < buildPhaseCount; buildPhaseIndex++) {
112-
auto buildPhaseID = (CFStringRef)CFArrayGetValueAtIndex(buildPhases, buildPhaseIndex);
113-
auto buildPhase = (CFDictionaryRef)CFDictionaryGetValue(objects, buildPhaseID);
114-
auto fileRefs = (CFArrayRef)CFDictionaryGetValue(buildPhase, CFSTR("files"));
88+
auto buildPhaseID = cf_string_ref(CFArrayGetValueAtIndex(buildPhases, buildPhaseIndex));
89+
auto buildPhase = cf_dictionary_ref(CFDictionaryGetValue(objects, buildPhaseID));
90+
auto fileRefs = cf_array_ref(CFDictionaryGetValue(buildPhase, CFSTR("files")));
11591
if (!fileRefs) {
11692
continue;
11793
}
11894
auto fileRefsCount = CFArrayGetCount(fileRefs);
11995
for (CFIndex fileRefIndex = 0; fileRefIndex < fileRefsCount; fileRefIndex++) {
120-
auto fileRefID = (CFStringRef)CFArrayGetValueAtIndex(fileRefs, fileRefIndex);
121-
auto fileRef = (CFDictionaryRef)CFDictionaryGetValue(objects, fileRefID);
122-
auto fileID = (CFStringRef)CFDictionaryGetValue(fileRef, CFSTR("fileRef"));
96+
auto fileRefID = cf_string_ref(CFArrayGetValueAtIndex(fileRefs, fileRefIndex));
97+
auto fileRef = cf_dictionary_ref(CFDictionaryGetValue(objects, fileRefID));
98+
auto fileID = cf_string_ref(CFDictionaryGetValue(fileRef, CFSTR("fileRef")));
12399
if (!fileID) {
124100
// FileRef is not a reference to a file (e.g., PBXBuildFile)
125101
continue;
126102
}
127-
auto file = (CFDictionaryRef)CFDictionaryGetValue(objects, fileID);
103+
auto file = cf_dictionary_ref(CFDictionaryGetValue(objects, fileID));
128104
if (!file) {
129105
// Sometimes the references file belongs to another project, which is not present for
130106
// various reasons
131107
continue;
132108
}
133-
auto isa = stringValue(file, CFSTR("isa"));
109+
auto isa = stringValueForKey(file, CFSTR("isa"));
134110
if (isa != "PBXFileReference") {
135111
// Skipping anything that is not a 'file', e.g. PBXVariantGroup
136112
continue;
137113
}
138-
auto fileType = stringValue(file, CFSTR("lastKnownFileType"));
139-
auto path = stringValue(file, CFSTR("path"));
114+
auto fileType = stringValueForKey(file, CFSTR("lastKnownFileType"));
115+
auto path = stringValueForKey(file, CFSTR("path"));
140116
if (fileType == "sourcecode.swift" && !path.empty()) {
141117
buildFiles[targetName].emplace_back(path, file);
142118
}
@@ -161,15 +137,13 @@ static CFDictionaryRef xcodeProjectObjects(const std::string& xcodeProject) {
161137
CFErrorRef error = nullptr;
162138
auto plist = CFPropertyListCreateWithData(allocator, data, 0, nullptr, &error);
163139
if (error) {
164-
auto description = CFCopyDescription(error);
165-
std::cerr << "[xcode autobuilder] Cannot read Xcode project: "
166-
<< CFStringGetCStringPtr(description, kCFStringEncodingUTF8) << ": " << pbxproj
167-
<< "\n";
168-
CFRelease(description);
140+
std::cerr << "[xcode autobuilder] Cannot read Xcode project: ";
141+
CFShow(error);
142+
std::cerr << ": " << pbxproj << "\n";
169143
return CFDictionaryCreate(allocator, nullptr, nullptr, 0, nullptr, nullptr);
170144
}
171145

172-
return (CFDictionaryRef)CFDictionaryGetValue((CFDictionaryRef)plist, CFSTR("objects"));
146+
return cf_dictionary_ref(CFDictionaryGetValue((CFDictionaryRef)plist, CFSTR("objects")));
173147
}
174148

175149
// Maps each target to the number of Swift source files it contains transitively
@@ -200,8 +174,8 @@ static std::vector<std::pair<std::string, std::string>> readTargets(const std::s
200174
for (size_t i = 0; i < kv.size; i++) {
201175
auto object = (CFDictionaryRef)kv.values[i];
202176
if (objectIsTarget(object)) {
203-
auto name = stringValue(object, CFSTR("name"));
204-
auto type = stringValue(object, CFSTR("productType"));
177+
auto name = stringValueForKey(object, CFSTR("name"));
178+
auto type = stringValueForKey(object, CFSTR("productType"));
205179
targets.emplace_back(name, type.empty() ? "<unknown_target_type>" : type);
206180
}
207181
}

swift/xcode-autobuilder/xcode-autobuilder.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#include <iostream>
2-
#include <unordered_map>
3-
#include <unordered_set>
42
#include <vector>
53
#include <filesystem>
64
#include "swift/xcode-autobuilder/XcodeTarget.h"
@@ -15,7 +13,7 @@ struct CLIArgs {
1513
bool dryRun;
1614
};
1715

18-
static void autobuild(CLIArgs args) {
16+
static void autobuild(const CLIArgs& args) {
1917
auto targets = collectTargets(args.workingDir);
2018

2119
// Filter out non-application/framework targets

0 commit comments

Comments
 (0)