├── BuildSettingExtractor.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ └── BuildSettingExtractor.xcscheme
├── BuildSettingExtractor
├── AppConstants+Categories.h
├── AppConstants+Categories.m
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── BuildSettingExtractorIcon-1024.png
│ │ ├── BuildSettingExtractorIcon-128.png
│ │ ├── BuildSettingExtractorIcon-16.png
│ │ ├── BuildSettingExtractorIcon-256.png
│ │ ├── BuildSettingExtractorIcon-32.png
│ │ ├── BuildSettingExtractorIcon-512.png
│ │ ├── BuildSettingExtractorIcon-64.png
│ │ └── Contents.json
│ ├── Contents.json
│ ├── config_file.imageset
│ │ ├── Contents.json
│ │ ├── config_file.png
│ │ └── config_file@2x.png
│ ├── contents_icon.imageset
│ │ ├── Contents.json
│ │ ├── contents_icon.png
│ │ └── contents_icon@2x.png
│ ├── dragViewBackgroundColor.colorset
│ │ └── Contents.json
│ ├── dragViewTextColor.colorset
│ │ └── Contents.json
│ └── folder.imageset
│ │ ├── Contents.json
│ │ ├── folder.png
│ │ └── folder@2x.png
├── Base.lproj
│ └── MainMenu.xib
├── BuildSettingCommentGenerator.h
├── BuildSettingCommentGenerator.m
├── BuildSettingExtractor-Info.plist
├── BuildSettingExtractor.entitlements
├── BuildSettingExtractor.h
├── BuildSettingExtractor.m
├── BuildSettingInfoSource.h
├── BuildSettingInfoSource.m
├── BuildSettingInfoSubpaths.plist
├── BuildSettingInfoSubpathsNotes.md
├── Constants+Categories.h
├── Constants+Categories.m
├── DragFileView.h
├── DragFileView.m
├── Preferences
│ ├── Base.lproj
│ │ └── Preferences.storyboard
│ ├── ContentsPreferencesViewController.h
│ ├── ContentsPreferencesViewController.m
│ ├── FileLayoutPreferencesViewController.h
│ ├── FileLayoutPreferencesViewController.m
│ ├── PreferencesViewController.h
│ ├── PreferencesViewController.m
│ ├── SampleFileStructureGenerator.h
│ └── SampleFileStructureGenerator.m
├── en.lproj
│ └── InfoPlist.strings
└── main.m
├── BuildSettingExtractorTests
├── BuildSettingExtractor.xctestplan
├── BuildSettingExtractorTests-Info.plist
├── BuildSettingExtractorTests.m
├── TestFiles
│ ├── BadProject.xcodeproj.test
│ │ └── project.pbxproj
│ ├── BadVersionNumber.xcodeproj.test
│ │ └── project.pbxproj
│ ├── ConflictingName.xcodeproj.test
│ │ └── project.pbxproj
│ ├── EmptySettings.xcodeproj.test
│ │ └── project.pbxproj
│ ├── FormattingTestData.plist
│ └── ThreeBuildConfigs.plist
└── en.lproj
│ └── InfoPlist.strings
├── Config
├── AppConfig.xcconfig
└── ProjectConfig.xcconfig
├── ReadMe.md
└── ci_scripts
└── ci_post_clone.sh
/BuildSettingExtractor.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/BuildSettingExtractor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BuildSettingExtractor.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/BuildSettingExtractor.xcodeproj/xcshareddata/xcschemes/BuildSettingExtractor.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
34 |
35 |
36 |
37 |
39 |
45 |
46 |
47 |
48 |
49 |
59 |
61 |
67 |
68 |
69 |
70 |
76 |
78 |
84 |
85 |
86 |
87 |
89 |
90 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/AppConstants+Categories.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppConstants+Categories.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 9/30/19.
6 | // Copyright © 2019 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #pragma mark User Default Keys
12 |
13 | extern NSString *const TPSOpenDirectoryInFinder;
14 | extern NSString *const TPSIncludeBuildSettingInfoComments;
15 | extern NSString *const TPSOutputFileNameProject;
16 | extern NSString *const TPSOutputFileNameShared;
17 | extern NSString *const TPSOutputFileNameSeparator;
18 | extern NSString *const TPSLinesBetweenBuildSettings;
19 | extern NSString *const TPSLinesBetweenBuildSettingsWithInfo;
20 | extern NSString *const TPSTargetFoldersEnabled;
21 | extern NSString *const TPSProjectFolderEnabled;
22 | extern NSString *const TPSDestinationFolderName;
23 | extern NSString *const TPSAutosaveInProjectFolder;
24 | extern NSString *const TPSAlignBuildSettingValues;
25 |
26 | @interface NSUserDefaults (TPS_DefaultsRegistration)
27 | - (void)tps_registerApplicationDefaults;
28 | @end
29 |
30 | #pragma mark -
31 |
32 | @interface NSPasteboard (TPS_XcodeProjectURLAdditions)
33 | - (BOOL)tps_canReadXcodeProjectFileURL;
34 | - (NSURL *)tps_readXcodeProjectFileURL;
35 | @end
36 |
37 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/AppConstants+Categories.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppConstants+Categories.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 9/30/19.
6 | // Copyright © 2019 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "AppConstants+Categories.h"
10 | #import "Constants+Categories.h"
11 | #import "BuildSettingExtractor.h"
12 |
13 | #pragma mark User Default Keys
14 |
15 | NSString *const TPSOpenDirectoryInFinder = @"TPSOpenDirectoryInFinder";
16 | NSString *const TPSIncludeBuildSettingInfoComments = @"TPSIncludeBuildSettingInfoComments";
17 | NSString *const TPSOutputFileNameProject = @"TPSOutputFileNameProject";
18 | NSString *const TPSOutputFileNameShared = @"TPSOutputFileNameShared";
19 | NSString *const TPSOutputFileNameSeparator = @"TPSOutputFileNameSeparator";
20 | NSString *const TPSLinesBetweenBuildSettings = @"TPSLinesBetweenBuildSettings";
21 | NSString *const TPSLinesBetweenBuildSettingsWithInfo = @"TPSLinesBetweenBuildSettingsWithInfo";
22 | NSString *const TPSTargetFoldersEnabled = @"TPSTargetFoldersEnabled";
23 | NSString *const TPSProjectFolderEnabled = @"TPSProjectFolderEnabled";
24 | NSString *const TPSDestinationFolderName = @"TPSDestinationFolderName";
25 | NSString *const TPSAutosaveInProjectFolder = @"TPSAutosaveInProjectFolder";
26 | NSString *const TPSAlignBuildSettingValues = @"TPSAlignBuildSettingValues";
27 |
28 | @implementation NSUserDefaults (TPS_DefaultsRegistration)
29 | - (void)tps_registerApplicationDefaults {
30 | NSDictionary *defaults = @{
31 | TPSOpenDirectoryInFinder : @(YES),
32 | TPSIncludeBuildSettingInfoComments : @(YES),
33 | TPSOutputFileNameShared : BuildSettingExtractor.defaultSharedConfigName,
34 | TPSOutputFileNameProject : BuildSettingExtractor.defaultProjectConfigName,
35 | TPSOutputFileNameSeparator : BuildSettingExtractor.defaultNameSeparator,
36 | TPSLinesBetweenBuildSettings : @0,
37 | TPSLinesBetweenBuildSettingsWithInfo : @3,
38 | TPSTargetFoldersEnabled : @(NO),
39 | TPSProjectFolderEnabled : @(NO),
40 | TPSDestinationFolderName : BuildSettingExtractor.defaultDestinationFolderName,
41 | TPSAutosaveInProjectFolder: @(NO),
42 | TPSAlignBuildSettingValues: @(NO)
43 | };
44 | [self registerDefaults:defaults];
45 | }
46 | @end
47 |
48 | #pragma mark -
49 |
50 | @implementation NSPasteboard (TPS_XcodeProjectURLAdditions)
51 |
52 | - (NSURL *)tps_readXcodeProjectFileURL {
53 | NSArray *readObjects = [self readObjectsForClasses:@[[NSURL class]] options:[self tps_xcodeProjectReadingOptions]];
54 | return readObjects.firstObject;
55 | }
56 |
57 | - (BOOL)tps_canReadXcodeProjectFileURL {
58 | return [self canReadObjectForClasses:@[[NSURL class]] options:[self tps_xcodeProjectReadingOptions]];
59 | }
60 |
61 | - (NSDictionary *)tps_xcodeProjectReadingOptions {
62 | return @{NSPasteboardURLReadingFileURLsOnlyKey: @(YES), NSPasteboardURLReadingContentsConformToTypesKey: @[[NSString tps_projectBundleTypeIdentifier]]};
63 | }
64 |
65 | @end
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 9/9/14.
6 | // Copyright (c) 2014 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Cocoa;
10 |
11 | @interface AppDelegate : NSObject
12 | @end
13 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 9/9/14.
6 | // Copyright (c) 2014 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "DragFileView.h"
11 | #import "BuildSettingExtractor.h"
12 | #import "AppConstants+Categories.h"
13 | #import "Constants+Categories.h"
14 |
15 | // During development it is useful to turn off the overwrite checking
16 | #define OVERWRITE_CHECKING_DISABLED 0
17 |
18 | @interface AppDelegate ()
19 |
20 | @property (weak) IBOutlet NSWindow *window;
21 | @property (weak) IBOutlet DragFileView *dragFileView;
22 | @property NSWindowController *preferencesWindowController;
23 | @property (weak) IBOutlet NSTextField *dragFileLabel;
24 |
25 | @end
26 |
27 | @implementation AppDelegate
28 |
29 | + (void)initialize {
30 | EmptyStringTransformer *transformer = [[EmptyStringTransformer alloc] init];
31 | [NSValueTransformer setValueTransformer:transformer forName:@"EmptyStringTransformer"];
32 | }
33 |
34 | - (void)awakeFromNib {
35 | self.dragFileView.target = self;
36 | self.dragFileView.action = @selector(handleDroppedFile:);
37 | if (@available(macOS 10.13, *)) {
38 | self.dragFileLabel.textColor = [NSColor colorNamed:@"dragViewTextColor"];
39 | } else {
40 | self.dragFileLabel.textColor = [NSColor colorWithCalibratedRed:0.13 green:0.26 blue:0.42 alpha:1.0];
41 | }
42 | }
43 |
44 |
45 | - (IBAction)chooseXcodeProject:(id)sender {
46 |
47 | NSOpenPanel *openPanel = [NSOpenPanel openPanel];
48 | openPanel.canCreateDirectories = NO;
49 | openPanel.allowsMultipleSelection = NO;
50 | openPanel.canChooseDirectories = NO;
51 | openPanel.canChooseFiles = YES;
52 | openPanel.allowedFileTypes = @[[NSString tps_projectBundleTypeIdentifier]];
53 | openPanel.message = @"Choose an Xcode project to extract its build settings.";
54 | openPanel.prompt = @"Choose";
55 |
56 | [openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
57 | if (result == NSModalResponseOK) {
58 | NSURL *projectURL = openPanel.URL;
59 | dispatch_async(dispatch_get_main_queue(), ^{
60 | [self processXcodeProjectAtURL:projectURL];
61 | });
62 | }
63 | }];
64 | }
65 |
66 | - (IBAction)handleDroppedFile:(DragFileView *)sender {
67 | NSURL *fileURL = sender.fileURL;
68 | [self processXcodeProjectAtURL:fileURL];
69 | }
70 |
71 | - (void)processXcodeProjectAtURL:(NSURL *)fileURL {
72 | NSString *typeIdentifier = nil;
73 | NSError *error = nil;
74 | [fileURL getResourceValue:&typeIdentifier forKey:NSURLTypeIdentifierKey error:&error];
75 |
76 | if (fileURL && [typeIdentifier isEqualToString:[NSString tps_projectBundleTypeIdentifier]]) {
77 | if ([[NSUserDefaults standardUserDefaults] boolForKey:TPSAutosaveInProjectFolder]) {
78 | NSURL *baseURL = [fileURL URLByDeletingLastPathComponent];
79 | NSURL *destinationURL = [self createValidatedDestinationURLForBaseURL:baseURL error:&error];
80 | if (!destinationURL) {
81 | NSAlert *alert = [NSAlert alertWithError:error];
82 | [alert beginSheetModalForWindow:self.window completionHandler:nil];
83 | return;
84 | }
85 | [self performExtractionFromProject:fileURL toDestination:destinationURL];
86 | } else {
87 | [self selectDestinationURLForSourceProject:fileURL];
88 | }
89 | }
90 | }
91 |
92 | - (void)selectDestinationURLForSourceProject:(NSURL *)fileURL {
93 |
94 | NSOpenPanel *openPanel = [NSOpenPanel openPanel];
95 | openPanel.canCreateDirectories = YES;
96 | openPanel.allowsMultipleSelection = YES;
97 | openPanel.canChooseDirectories = YES;
98 | openPanel.canChooseFiles = NO;
99 | openPanel.allowedFileTypes = @[(NSString *)kUTTypeFolder];
100 | openPanel.message = [NSString stringWithFormat:@"Choose location to save configuration files.\nConfiguration files for project ‘%@’\nwill be saved in a folder named '%@'.", [fileURL lastPathComponent], [[NSUserDefaults standardUserDefaults] stringForKey:TPSDestinationFolderName]];
101 | openPanel.prompt = @"Choose";
102 |
103 | [openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
104 |
105 | if (result == NSModalResponseOK) {
106 | NSError *error = nil;
107 | NSURL *baseURL = openPanel.URL;
108 | NSURL *destinationURL = [self createValidatedDestinationURLForBaseURL:baseURL error: &error];
109 | if (!destinationURL) {
110 | NSAlert *alert = [NSAlert alertWithError:error];
111 | [alert beginSheetModalForWindow:self.window completionHandler:nil];
112 | return;
113 | }
114 |
115 | BOOL validDestination = [BuildSettingExtractor validateDestinationFolder:destinationURL error:&error];
116 |
117 | if (validDestination || OVERWRITE_CHECKING_DISABLED) {
118 | [self performExtractionFromProject:fileURL toDestination:destinationURL];
119 | }
120 | else {
121 | NSAlert *alert = [NSAlert alertWithError:error];
122 | [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
123 | if (returnCode == NSAlertSecondButtonReturn) {
124 | [self performExtractionFromProject:fileURL toDestination:destinationURL];
125 | }
126 | }];
127 | }
128 | }
129 | }];
130 | }
131 |
132 | - (nullable NSURL *)createValidatedDestinationURLForBaseURL:(NSURL *) baseURL error: (NSError **)error {
133 | NSString *folderName = [[NSUserDefaults standardUserDefaults] stringForKey:TPSDestinationFolderName];
134 | // First check if there is a folder of that name in the base url
135 | NSArray *fileURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:baseURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];
136 | NSMutableIndexSet *untakenIndexes = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(1, 9999)];
137 | BOOL foundNameConflict = NO;
138 | for (NSURL *fileURL in fileURLs) {
139 | NSString *filename = [fileURL lastPathComponent];
140 | if ([folderName isEqualToString:filename]) {
141 | foundNameConflict = YES;
142 | }
143 | if ([filename hasPrefix:folderName]) {
144 | NSArray *components = [filename componentsSeparatedByString:@"-"];
145 | if (components.count > 1) {
146 | NSUInteger value = (NSUInteger)[[components lastObject] integerValue];
147 | if (value != 0) {
148 | [untakenIndexes removeIndex:value];
149 | }
150 | }
151 | }
152 | }
153 | NSString *validatedFolderName = nil;
154 | if (foundNameConflict) {
155 | validatedFolderName = [NSString stringWithFormat:@"%@-%ld", folderName, untakenIndexes.firstIndex];
156 | } else {
157 | validatedFolderName = folderName;
158 | }
159 |
160 | // Then create destination folder
161 | NSURL *validatedDestinationURL = [baseURL URLByAppendingPathComponent:validatedFolderName];
162 | if ([[NSFileManager defaultManager] createDirectoryAtURL:validatedDestinationURL withIntermediateDirectories:NO attributes:nil error:error]) {
163 | return validatedDestinationURL;
164 | } else {
165 | return nil;
166 | }
167 | }
168 |
169 | - (void)performExtractionFromProject:(NSURL *)fileURL toDestination:(NSURL *)destinationURL {
170 |
171 | // Perform the extraction in the background.
172 | // Using DISPATCH_QUEUE_PRIORITY_HIGH which is available on 10.9
173 | // Move to QOS_CLASS_USER_INITIATED when 10.10 is the deployment target
174 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
175 | BuildSettingExtractor *buildSettingExtractor = [[BuildSettingExtractor alloc] init];
176 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
177 | buildSettingExtractor.sharedConfigName = [defaults stringForKey:TPSOutputFileNameShared];
178 | buildSettingExtractor.projectConfigName = [defaults stringForKey:TPSOutputFileNameProject];
179 | buildSettingExtractor.nameSeparator = [defaults stringForKey:TPSOutputFileNameSeparator];
180 | BOOL includeInfo = [defaults boolForKey:TPSIncludeBuildSettingInfoComments];
181 | buildSettingExtractor.includeBuildSettingInfoComments = includeInfo;
182 | buildSettingExtractor.linesBetweenSettings = [defaults integerForKey:includeInfo ? TPSLinesBetweenBuildSettingsWithInfo : TPSLinesBetweenBuildSettings];
183 | buildSettingExtractor.targetFoldersEnabled = [defaults boolForKey:TPSTargetFoldersEnabled];
184 | buildSettingExtractor.projectFolderEnabled = [defaults boolForKey:TPSProjectFolderEnabled];
185 | buildSettingExtractor.alignBuildSettingValues = [defaults boolForKey:TPSAlignBuildSettingValues];
186 |
187 | NSError *fatalError = nil;
188 |
189 | // Extract the build settings
190 | NSArray *nonFatalErrors = [buildSettingExtractor extractBuildSettingsFromProject:fileURL error:&fatalError];
191 |
192 | // On extraction fatal error, present the error and return.
193 | if (!nonFatalErrors && fatalError) {
194 | // present error on main thread and return
195 | dispatch_async(dispatch_get_main_queue(), ^{
196 | [NSApp presentError:fatalError];
197 | });
198 | return; // Can't continue, fatal error.
199 |
200 | }
201 | // Otherwise, present non-fatal errors, if present.
202 | else if (nonFatalErrors && nonFatalErrors.count > 0) {
203 | // present non-fatal errors on main thread
204 | dispatch_async(dispatch_get_main_queue(), ^{
205 | for (NSError *anError in nonFatalErrors) {
206 | [NSApp presentError:anError]; // Will present one at a time.
207 | }
208 | });
209 | }
210 |
211 | // Write the config files
212 | BOOL success = [buildSettingExtractor writeConfigFilesToDestinationFolder: destinationURL error: &fatalError];
213 | if (!success && fatalError) {
214 | // present error on main thread and return
215 | dispatch_async(dispatch_get_main_queue(), ^{
216 | [NSApp presentError:fatalError];
217 | });
218 | return; // Can't continue, fatal error.
219 | }
220 |
221 | BOOL openInFinder = [[NSUserDefaults standardUserDefaults] boolForKey:TPSOpenDirectoryInFinder];
222 | if (success && openInFinder) {
223 | [[NSWorkspace sharedWorkspace] openURL:destinationURL];
224 | }
225 | });
226 | }
227 |
228 | - (IBAction)presentPreferencesWindow:(id)sender {
229 | if (!self.preferencesWindowController) {
230 | NSStoryboard *storyboard = [NSStoryboard storyboardWithName:@"Preferences" bundle:nil];
231 | self.preferencesWindowController = (NSWindowController *)[storyboard instantiateInitialController];
232 | }
233 | [self.preferencesWindowController showWindow:nil];
234 | }
235 |
236 |
237 | #pragma mark - NSApplicationDelegate
238 |
239 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
240 | return YES;
241 | }
242 |
243 | - (void)applicationWillFinishLaunching:(NSNotification *)notification {
244 | [[NSUserDefaults standardUserDefaults] tps_registerApplicationDefaults];
245 | }
246 |
247 | - (void)application:(NSApplication *)application openURLs:(NSArray *)urls {
248 | NSURL *fileURL = urls.firstObject;
249 | if (fileURL) { [self processXcodeProjectAtURL:fileURL]; }
250 | }
251 |
252 | @end
253 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-1024.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-128.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-16.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-256.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-32.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-512.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/BuildSettingExtractorIcon-64.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "BuildSettingExtractorIcon-16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "BuildSettingExtractorIcon-32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "BuildSettingExtractorIcon-32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "BuildSettingExtractorIcon-64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "BuildSettingExtractorIcon-128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "BuildSettingExtractorIcon-256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "BuildSettingExtractorIcon-256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "BuildSettingExtractorIcon-512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "BuildSettingExtractorIcon-512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "BuildSettingExtractorIcon-1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/config_file.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "config_file.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "config_file@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/config_file.imageset/config_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/config_file.imageset/config_file.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/config_file.imageset/config_file@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/config_file.imageset/config_file@2x.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/contents_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "contents_icon.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "contents_icon@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/contents_icon.imageset/contents_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/contents_icon.imageset/contents_icon.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/contents_icon.imageset/contents_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/contents_icon.imageset/contents_icon@2x.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/dragViewBackgroundColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "mac",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.700",
13 | "alpha" : "1.000",
14 | "blue" : "1.000",
15 | "green" : "0.850"
16 | }
17 | }
18 | },
19 | {
20 | "idiom" : "mac",
21 | "appearances" : [
22 | {
23 | "appearance" : "luminosity",
24 | "value" : "dark"
25 | }
26 | ],
27 | "color" : {
28 | "color-space" : "srgb",
29 | "components" : {
30 | "red" : "0.130",
31 | "alpha" : "1.000",
32 | "blue" : "0.420",
33 | "green" : "0.260"
34 | }
35 | }
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/dragViewTextColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "mac",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.130",
13 | "alpha" : "1.000",
14 | "blue" : "0.420",
15 | "green" : "0.260"
16 | }
17 | }
18 | },
19 | {
20 | "idiom" : "mac",
21 | "appearances" : [
22 | {
23 | "appearance" : "luminosity",
24 | "value" : "dark"
25 | }
26 | ],
27 | "color" : {
28 | "color-space" : "srgb",
29 | "components" : {
30 | "red" : "0.700",
31 | "alpha" : "1.000",
32 | "blue" : "1.000",
33 | "green" : "0.850"
34 | }
35 | }
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/folder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "folder.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "folder@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/folder.imageset/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/folder.imageset/folder.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Assets.xcassets/folder.imageset/folder@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dempseyatgithub/BuildSettingExtractor/bd95f3d147bd5289068ba4048925f18220b85a5d/BuildSettingExtractor/Assets.xcassets/folder.imageset/folder@2x.png
--------------------------------------------------------------------------------
/BuildSettingExtractor/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingCommentGenerator.h:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingCommentGenerator.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/3/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | @class BuildSettingInfoSource;
12 |
13 | @interface BuildSettingCommentGenerator : NSObject
14 |
15 | - (instancetype)initWithBuildSettingInfoSource:(BuildSettingInfoSource *)infoSource;
16 |
17 | - (NSString *)commentForBuildSettingWithName:(NSString *)buildSettingName;
18 |
19 | - (BOOL)loadBuildSettingInfo:(NSError **)error; // In public interface for testing
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingCommentGenerator.m:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingCommentGenerator.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/3/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | #define MAX_LINE_LENGTH 87 // For 90 columns, minus three (comment slashes and a space)
10 |
11 | #import "BuildSettingCommentGenerator.h"
12 | #import "BuildSettingInfoSource.h"
13 | #import "Constants+Categories.h"
14 |
15 | @interface BuildSettingCommentGenerator ()
16 |
17 | @property (strong, nonatomic) BuildSettingInfoSource *infoSource;
18 |
19 | @property (strong, nonatomic) NSDictionary *buildSettingInfoDictionary;
20 |
21 | // Some build setting info uses the Xcode flavor of Markdown for option lists.
22 | // In Xcode, these show up as bulletted lists with option names emphasized.
23 | // In plain text the emphasis does not look as good.
24 | // This regex matches the common pattern "* *OptionName:*" and replaces it with "* OptionName:"
25 | @property (strong, nonatomic) NSRegularExpression *regex;
26 |
27 | @end
28 |
29 |
30 | @implementation BuildSettingCommentGenerator
31 |
32 | - (instancetype)init
33 | {
34 | [NSException raise:NSInternalInconsistencyException format:@"You must use -initWithBuildSettingInfoSource: to initialize instances of BuildSettingCommentGenerator."];
35 | return nil;
36 | }
37 |
38 | - (instancetype)initWithBuildSettingInfoSource:(BuildSettingInfoSource *)infoSource
39 | {
40 | self = [super init];
41 | if (self) {
42 | self.regex = [NSRegularExpression regularExpressionWithPattern:@"^\\*\\h\\*(.+?)\\*" options:NSRegularExpressionAnchorsMatchLines error:nil];
43 | self.infoSource = infoSource;
44 | }
45 | return self;
46 | }
47 |
48 | - (NSString *)commentForBuildSettingWithName:(NSString *)buildSettingName {
49 | NSMutableString *comment = [[NSMutableString alloc] init];
50 |
51 | NSString *presentationName = [self presentationNameForKey:buildSettingName];
52 | NSString *settingDescription = [self descriptionForKey:buildSettingName];
53 |
54 | if (presentationName || settingDescription) {
55 |
56 | if (settingDescription) {
57 | settingDescription = [self processedDescriptionString:settingDescription forKey:buildSettingName];
58 | }
59 | if (presentationName) {
60 | [comment appendFormat:@"// %@\n", presentationName];
61 | }
62 | if (settingDescription) {
63 | [comment appendFormat:@"// %@\n", settingDescription];
64 | }
65 | [comment appendString:@"\n"];
66 | }
67 |
68 | return comment;
69 | }
70 |
71 | - (NSString *)presentationNameForKey:(NSString *)key {
72 | if (!self.buildSettingInfoDictionary) {
73 | [self loadBuildSettingInfo:nil];
74 | }
75 |
76 | NSString *processedKey = [key tps_baseBuildSettingName]; // strip any conditional part of build setting
77 |
78 | processedKey = [self displayNameKeyForSetting:processedKey];
79 | return self.buildSettingInfoDictionary[processedKey];
80 | }
81 |
82 | - (NSString *)descriptionForKey:(NSString *)key {
83 | if (!self.buildSettingInfoDictionary) {
84 | [self loadBuildSettingInfo:nil];
85 | }
86 |
87 | NSString *processedKey = [key tps_baseBuildSettingName]; // strip any conditional part of build setting
88 |
89 | processedKey = [self descriptionKeyForSetting:processedKey];
90 | return self.buildSettingInfoDictionary[processedKey];
91 | }
92 |
93 | - (NSString *)processedCurrentLine:(NSString *)currentLine {
94 |
95 | // Trim whitespace
96 | currentLine = [currentLine stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
97 |
98 | // Process Markdown option list item
99 | if ([currentLine hasPrefix:@"*"]) {
100 | currentLine = [self.regex stringByReplacingMatchesInString:currentLine options:0 range:NSMakeRange(0, [currentLine length]) withTemplate:@"* $1"];
101 | }
102 |
103 | // Add newline and comment prefix
104 | currentLine = [NSString stringWithFormat:@"\n// %@", currentLine];
105 |
106 | return currentLine;
107 | }
108 |
109 | - (NSString *)processedDescriptionString:(NSString *)string forKey:(NSString *)key {
110 |
111 | NSString *processedString = @"";
112 |
113 | // Take the repetition of the build setting name out of the description.
114 | NSString *baseBuildSettingName = [key tps_baseBuildSettingName];
115 | NSString *buildNameString = [NSString stringWithFormat:@"[%@]", baseBuildSettingName];
116 | string = [string stringByReplacingOccurrencesOfString:buildNameString withString:@""];
117 |
118 | buildNameString = [NSString stringWithFormat:@"[%@, ", baseBuildSettingName];
119 | string = [string stringByReplacingOccurrencesOfString:buildNameString withString:@"["];
120 | string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
121 |
122 | for (NSString *paragraphString in [string componentsSeparatedByString:@"\n"]) {
123 |
124 |
125 | // Yes, this is not using grapheme clusters and assumes one char is one character.
126 | // It also assumes spaces separate words, which is not true in some languages.
127 | NSInteger characterCount = 0;
128 | NSString *currentLine = @"";
129 |
130 | for (NSString *wordish in [paragraphString componentsSeparatedByString:@" "]) {
131 | characterCount += wordish.length;
132 | if (characterCount < MAX_LINE_LENGTH) {
133 | currentLine = [currentLine stringByAppendingFormat:@"%@ ", wordish];
134 | characterCount++; // Account for the space
135 | } else {
136 | // Process current line
137 | currentLine = [self processedCurrentLine:currentLine];
138 | // Add to processed string
139 | processedString = [processedString stringByAppendingString:currentLine];
140 | // reset line count and set current line to token that didn't fit.
141 | characterCount = wordish.length + 1;
142 | currentLine = [NSString stringWithFormat:@"%@ ", wordish];
143 | }
144 | }
145 |
146 | // Append the most recent line if there is any
147 | if (![currentLine isEqualToString:@""]) {
148 | // Process current line
149 | currentLine = [self processedCurrentLine:currentLine];
150 | // Add to processed string
151 | processedString = [processedString stringByAppendingString:currentLine];
152 | }
153 |
154 | }
155 |
156 | return processedString;
157 | }
158 |
159 | // Returns an array of error strings describing unread build setting info files
160 | - (BOOL)loadBuildSettingInfo:(NSError **)error {
161 |
162 | NSMutableArray *subpathReadingErrorStrings = [[NSMutableArray alloc] init];
163 |
164 | NSString *defaultXcodePath = self.infoSource.resolvedURL.path;
165 | NSInteger xcodeVersion = self.infoSource.resolvedVersion;
166 |
167 | // Load subpaths
168 | NSURL *buildSettingInfoPlistURL = [[NSBundle mainBundle] URLForResource:@"BuildSettingInfoSubpaths" withExtension:@"plist"];
169 | NSDictionary *buildSettingInfoDict = [NSDictionary dictionaryWithContentsOfURL:buildSettingInfoPlistURL];
170 | NSArray *buildSettingInfoSubpaths = buildSettingInfoDict[@"subpaths"];
171 |
172 | NSMutableDictionary *infoStringFile = [NSMutableDictionary new];
173 |
174 | // A spot to put additional setting info. If a more official version is read in, it replaces the backstop info.
175 | NSDictionary *backstopSettingsInfo = buildSettingInfoDict[@"backstopSettingInfo"];
176 | [infoStringFile addEntriesFromDictionary:backstopSettingsInfo];
177 |
178 | // Add deprecated subpaths that are valid in the version of Xcode we are using
179 | NSMutableArray *subpathsToAdd = nil;
180 | NSDictionary *deprecatedSubpaths = buildSettingInfoDict[@"deprecatedSubpathsByVersion"];
181 | for (NSString *versionKey in [deprecatedSubpaths allKeys]) {
182 | NSInteger maxVersion = [versionKey integerValue];
183 | if (xcodeVersion < maxVersion) {
184 | if (!subpathsToAdd) {
185 | subpathsToAdd = [NSMutableArray array];
186 | }
187 | [subpathsToAdd addObjectsFromArray:deprecatedSubpaths[versionKey]];
188 | }
189 | }
190 | if (subpathsToAdd) {
191 | buildSettingInfoSubpaths = [buildSettingInfoSubpaths arrayByAddingObjectsFromArray:subpathsToAdd];
192 | }
193 |
194 | // Add introduced subpaths that are valid in the version of Xcode we are using
195 | subpathsToAdd = nil;
196 | NSDictionary *introducedSubpaths = buildSettingInfoDict[@"introducedSubpathsByVersion"];
197 | for (NSString *versionKey in [introducedSubpaths allKeys]) {
198 | NSInteger minVersion = [versionKey integerValue];
199 | if (xcodeVersion >= minVersion) {
200 | if (!subpathsToAdd) {
201 | subpathsToAdd = [NSMutableArray array];
202 | }
203 | [subpathsToAdd addObjectsFromArray:introducedSubpaths[versionKey]];
204 | }
205 | }
206 | if (subpathsToAdd) {
207 | buildSettingInfoSubpaths = [buildSettingInfoSubpaths arrayByAddingObjectsFromArray:subpathsToAdd];
208 | }
209 |
210 |
211 | // Rather than track exactly what Xcode versions contain which files, group versions of an expected file in an array.
212 | // Log if no file in the group can be read in.
213 | for (NSArray *buildSettingInfoSubpathList in buildSettingInfoSubpaths) {
214 | BOOL foundOne = NO;
215 | for (NSString *subpath in buildSettingInfoSubpathList) {
216 | NSString *pathExtension = [subpath pathExtension];
217 | NSString *fullpath = [defaultXcodePath stringByAppendingPathComponent:subpath];
218 | NSDictionary *dictionary = nil;
219 | if ([pathExtension isEqualToString:@"strings"]) {
220 | dictionary = [NSDictionary dictionaryWithContentsOfFile:fullpath];
221 | } else if ([pathExtension isEqualToString:@"xcspec"]) {
222 | dictionary = [self xcspecFileBuildSettingInfoForPath:fullpath];
223 | }
224 | if (dictionary) {
225 | // Remove empty string values that may be masking backstop values
226 | NSDictionary *processedDictionary = [dictionary tps_dictionaryByRemovingEmptyStringValues];
227 | [infoStringFile addEntriesFromDictionary:processedDictionary];
228 | }
229 | if (dictionary || foundOne) {
230 | foundOne = YES;
231 | }
232 | }
233 | if (!foundOne) {
234 | NSString *errorString = nil;
235 | if (buildSettingInfoSubpathList.count == 0) {
236 | errorString = [NSString stringWithFormat: @"Empty array of subpaths at index %lu", [buildSettingInfoSubpaths indexOfObject:buildSettingInfoSubpathList]];
237 | } else if (buildSettingInfoSubpathList.count == 1) {
238 | errorString = [NSString stringWithFormat:@"Could not read settings strings at path: %@", buildSettingInfoSubpathList[0]];
239 | } else {
240 | errorString = [NSString stringWithFormat:@"Could not read settings strings at these paths: %@", buildSettingInfoSubpathList];
241 | }
242 | [subpathReadingErrorStrings addObject:errorString];
243 | }
244 | }
245 |
246 | _buildSettingInfoDictionary = infoStringFile;
247 |
248 | BOOL success = subpathReadingErrorStrings.count == 0;
249 |
250 | if (!success && error) {
251 | *error = [NSError errorForSettingInfoFilesNotFound:subpathReadingErrorStrings];
252 | }
253 |
254 | return success;
255 | }
256 |
257 | - (NSDictionary *)xcspecFileBuildSettingInfoForPath:(NSString *)path {
258 | NSURL *fileURL = [NSURL fileURLWithPath:path];
259 | NSMutableDictionary *buildSettingInfo = [NSMutableDictionary dictionary];
260 | NSError *error = nil;
261 | NSData *plistData = [[NSData alloc] initWithContentsOfURL:fileURL];
262 | // If we can't read the file, behave like +dictionaryWithContentsOfFile: and return nil
263 | if (!plistData) {
264 | return nil;
265 | }
266 | id plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:nil error:&error];
267 | if ([plist isKindOfClass:[NSArray class]]) {
268 | NSArray *specs = (NSArray *)plist;
269 | for (id spec in specs) {
270 | if ([spec isKindOfClass:[NSDictionary class]]) {
271 | NSDictionary *specDict = (NSDictionary *)spec;
272 | NSString *specType = specDict[@"Type"];
273 | if ([specType isEqualToString:@"BuildSystem"] || ([specType isEqualToString:@"Compiler"])) {
274 |
275 | id options = specDict[@"Options"];
276 | if (!options) options = specDict[@"Properties"];
277 |
278 | if ([options isKindOfClass:[NSArray class]]) {
279 | for (id option in options) {
280 | if ([option isKindOfClass:[NSDictionary class]]) {
281 | NSString *name = option[@"Name"];
282 | NSString *desc = option[@"Description"];
283 | NSString *displayName = option[@"DisplayName"];
284 |
285 | if (name && desc && displayName) {
286 | [buildSettingInfo setValue:displayName forKey:[self displayNameKeyForSetting:name]];
287 | [buildSettingInfo setValue:desc forKey:[self descriptionKeyForSetting:name]];
288 | }
289 | }
290 | }
291 | }
292 | }
293 |
294 | }
295 | }
296 | }
297 |
298 | return buildSettingInfo;
299 | }
300 |
301 | - (NSString *)displayNameKeyForSetting:(NSString *)settingName {
302 | return [NSString stringWithFormat:@"[%@]-name", settingName];
303 | }
304 |
305 | - (NSString *)descriptionKeyForSetting:(NSString *)settingName {
306 | return [NSString stringWithFormat:@"[%@]-description", settingName];
307 | }
308 |
309 | @end
310 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingExtractor-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDocumentTypes
8 |
9 |
10 | CFBundleTypeExtensions
11 |
12 | xcodeproj
13 |
14 | CFBundleTypeRole
15 | Editor
16 | LSItemContentTypes
17 |
18 | com.apple.xcode.project
19 |
20 |
21 |
22 | CFBundleExecutable
23 | ${EXECUTABLE_NAME}
24 | CFBundleIconFile
25 |
26 | CFBundleIdentifier
27 | $(PRODUCT_BUNDLE_IDENTIFIER)
28 | CFBundleInfoDictionaryVersion
29 | 6.0
30 | CFBundleName
31 | ${PRODUCT_NAME}
32 | CFBundlePackageType
33 | APPL
34 | CFBundleShortVersionString
35 | $(MARKETING_VERSION)
36 | CFBundleSignature
37 | ????
38 | CFBundleVersion
39 | $(CURRENT_PROJECT_VERSION)
40 | LSApplicationCategoryType
41 | public.app-category.developer-tools
42 | LSMinimumSystemVersion
43 | ${MACOSX_DEPLOYMENT_TARGET}
44 | NSHumanReadableCopyright
45 | Copyright © 2014-2025 Tapas Software. All rights reserved.
46 | NSMainNibFile
47 | MainMenu
48 | NSPrincipalClass
49 | NSApplication
50 |
51 |
52 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingExtractor.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-write
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingExtractor.h:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingExtractor.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface BuildSettingExtractor : NSObject
14 |
15 | /* The generated file names take the form '.xcconfig'
16 |
17 | For the piece:
18 | Target build setting files use the name of the target.
19 | Project build setting files use the value of -projectConfigName.
20 |
21 | For the piece:
22 | Files containing the settings unique to a Build Configuration use the name of the build configuration.
23 | Files containing settings common to all build configurations for a target use the value of -sharedConfigName.
24 |
25 | For the piece:
26 | The separator used is the value of -nameSeparator.
27 | */
28 |
29 | // The default values for naming of generated files. Potentially useful to register as defaults in an app.
30 | + (NSString *)defaultSharedConfigName; // "Shared" is the default.
31 | + (NSString *)defaultProjectConfigName; // "Project" is the default.
32 | + (NSString *)defaultNameSeparator; // "-" (hyphen) is the default.
33 | + (NSString *)defaultDestinationFolderName; // "xcconfig" is the default.
34 |
35 |
36 | // The name that will be used to name common / shared config files.
37 | @property (copy) NSString *sharedConfigName;
38 |
39 | // The name that will be used to name the project configuration.
40 | @property (copy) NSString *projectConfigName;
41 |
42 | // The string that will separate filename components.
43 | @property (copy) NSString *nameSeparator;
44 |
45 | // Number of lines between build settings. Default is 0.
46 | @property NSInteger linesBetweenSettings;
47 |
48 |
49 | // Should each build setting be commented with title and description, if available. Default is NO.
50 | @property (assign) BOOL includeBuildSettingInfoComments;
51 |
52 | // If set to YES the generated config files for each target will be put in a separate folder.
53 | // Use with the projectFolderEnabled property for project-level config files. Default is NO.
54 | @property BOOL targetFoldersEnabled;
55 |
56 | // If set to YES project-level config files will be put in a separate folder.
57 | // Only takes effect when targetFoldersEnabled is also set to YES. Default is NO.
58 | @property BOOL projectFolderEnabled;
59 |
60 | // If set to YES aligns the build settings in generated files. Default is NO.
61 | @property BOOL alignBuildSettingValues;
62 |
63 | // Validates destination folder, checking for existing build config files
64 | // Returns an error suitable for presentation in an alert with options to cancel or replace existing files.
65 | + (BOOL)validateDestinationFolder:(NSURL *)destinationURL error:(NSError **)error;
66 |
67 | // Extracts the build settings from the project.
68 | // Returns an array of zero or more non-fatal validation errors or nil if a fatal error is encountered
69 | - (nullable NSArray *)extractBuildSettingsFromProject:(NSURL *)projectWrapperURL error:(NSError **)error;
70 |
71 | // After successful extraction, writes config files to the destination folder
72 | // Returns whether the method was successful and an error if unsuccessful
73 | //
74 | // NOTE: This method will throw an exception if it is called before successful extraction of build settings
75 | // by successful completion of -extractBuildSettingsFromProject:error:. Callers must call -extractBuildSettingsFromProject:error: first and check for a non-nil return value indicating success.
76 | - (BOOL)writeConfigFilesToDestinationFolder:(NSURL *)destinationURL error:(NSError **)error;
77 |
78 | // Generates an example build setting string using the provided settings dictionary and options. The settings dictionary keys are expected
79 | // to be string. The values are expected to be strings or arrays of strings.
80 | + (NSString *)exampleBuildFormattingStringForSettings:(NSDictionary *)settings includeBuildSettingInfoComments:(BOOL)includeBuildSettingInfoComments alignBuildSettingValues:(BOOL)alignBuildSettingValues linesBetweenSettings:(NSInteger)linesBetweenSettings;
81 |
82 | @end
83 |
84 | NS_ASSUME_NONNULL_END
85 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingExtractor.m:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingExtractor.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "BuildSettingExtractor.h"
10 | #import "BuildSettingCommentGenerator.h"
11 | #import "BuildSettingInfoSource.h"
12 | #import "Constants+Categories.h"
13 |
14 | static NSSet *XcodeCompatibilityVersionStringSet(void) {
15 | static NSSet *_compatibilityVersionStringSet;
16 | static dispatch_once_t onceToken;
17 | dispatch_once(&onceToken, ^{
18 | _compatibilityVersionStringSet = [NSSet setWithObjects:@"Xcode 3.2", @"Xcode 6.3", @"Xcode 8.0", @"Xcode 9.3", @"Xcode 10.0", @"Xcode 11.0", @"Xcode 11.4", @"Xcode 12.0", @"Xcode 13.0", @"Xcode 14.0", @"Xcode 15.0", @"Xcode 15.3", @"Xcode 16.0", nil];
19 | });
20 | return _compatibilityVersionStringSet;
21 | }
22 |
23 | static NSSet *XcodeObjectVersionStringSet(void) {
24 | static NSSet *_objectVersionStringSet;
25 | static dispatch_once_t onceToken;
26 | dispatch_once(&onceToken, ^{
27 | _objectVersionStringSet = [NSSet setWithObjects:@"77", @"70", nil];
28 | });
29 | return _objectVersionStringSet;
30 | }
31 |
32 | @interface BuildSettingExtractor ()
33 | @property (strong) NSMutableDictionary *buildSettingsByTarget;
34 | @property (strong) NSDictionary *objects;
35 | @property (nullable) NSString *validatedProjectConfigName;
36 | @property BOOL extractionSuccessful;
37 |
38 | @property (strong) BuildSettingCommentGenerator *buildSettingCommentGenerator;
39 | @end
40 |
41 | @implementation BuildSettingExtractor
42 |
43 | + (NSString *)defaultSharedConfigName {
44 | return @"Shared";
45 | }
46 |
47 | + (NSString *)defaultProjectConfigName {
48 | return @"Project";
49 | }
50 |
51 | + (NSString *)defaultNameSeparator {
52 | return @"-";
53 | }
54 |
55 | + (NSString *)defaultDestinationFolderName {
56 | return @"xcconfig";
57 | }
58 |
59 | - (instancetype)init {
60 | self = [super init];
61 | if (self) {
62 | _sharedConfigName = [[self class] defaultSharedConfigName];
63 | _projectConfigName = [[self class] defaultProjectConfigName];
64 | _nameSeparator = [[self class] defaultNameSeparator];
65 | _buildSettingsByTarget = [[NSMutableDictionary alloc] init];
66 | _buildSettingCommentGenerator = nil;
67 | _validatedProjectConfigName = nil;
68 | _extractionSuccessful = NO;
69 | }
70 | return self;
71 | }
72 |
73 | /* Given a dictionary and key whose value is an array of object identifiers, return the identified objects in an array */
74 | - (NSArray *)objectArrayForDictionary:(NSDictionary *)dict key:(NSString *)key {
75 | NSArray *identifiers = dict[key];
76 | NSMutableArray *objectArray = [[NSMutableArray alloc] init];
77 | for (NSString *identifier in identifiers) {
78 | id obj = self.objects[identifier];
79 | [objectArray addObject:obj];
80 | }
81 | return objectArray;
82 | }
83 |
84 |
85 | + (BOOL)validateDestinationFolder:(NSURL *)destinationURL error:(NSError **)error {
86 |
87 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:destinationURL includingPropertiesForKeys:@[NSURLTypeIdentifierKey] options:NSDirectoryEnumerationSkipsHiddenFiles | NSDirectoryEnumerationSkipsPackageDescendants errorHandler:nil];
88 |
89 | BOOL foundBuildConfigFile = NO;
90 |
91 | NSURL *currentURL = nil;
92 | while (currentURL = [enumerator nextObject]) {
93 | // Only check three levels deep, in case user chooses something like the home folder
94 | if (enumerator.level > 3) {
95 | [enumerator skipDescendants];
96 | continue;
97 | }
98 | NSString *typeIdentifier = nil;
99 | NSError *resourceError = nil;
100 | [currentURL getResourceValue:&typeIdentifier forKey:NSURLTypeIdentifierKey error:&resourceError];
101 | if ([typeIdentifier isEqualToString:[NSString tps_buildConfigurationFileTypeIdentifier]]) {
102 | foundBuildConfigFile = YES;
103 | break;
104 | }
105 | }
106 |
107 | BOOL isValid = !foundBuildConfigFile;
108 |
109 | if (foundBuildConfigFile) {
110 | if (error) {
111 | *error = [NSError errorForDestinationContainsBuildConfigFiles];
112 | }
113 | }
114 |
115 | return isValid;
116 | }
117 |
118 |
119 | - (NSArray *)extractBuildSettingsFromProject:(NSURL *)projectWrapperURL error:(NSError **)error {
120 |
121 | NSMutableArray *nonFatalErrors = [[NSMutableArray alloc] init];
122 |
123 | [self.buildSettingsByTarget removeAllObjects];
124 |
125 | if (self.includeBuildSettingInfoComments) {
126 |
127 | NSError *infoSourceError = nil;
128 | BuildSettingInfoSource *infoSource = [BuildSettingInfoSource resolvedBuildSettingInfoSourceWithStyle:BuildSettingInfoSourceStyleStandard customURL:nil error:&infoSourceError];
129 |
130 | if (infoSource) {
131 | self.buildSettingCommentGenerator = [[BuildSettingCommentGenerator alloc] initWithBuildSettingInfoSource:infoSource];
132 | } else {
133 | // If no info source, fallback to basic formatting
134 | self.includeBuildSettingInfoComments = NO;
135 | self.linesBetweenSettings = 0;
136 | }
137 |
138 | if (infoSourceError) {
139 | [nonFatalErrors addObject:infoSourceError];
140 | }
141 | }
142 |
143 | NSURL *projectFileURL = [projectWrapperURL URLByAppendingPathComponent:@"project.pbxproj"];
144 |
145 | NSData *fileData = [NSData dataWithContentsOfURL:projectFileURL options:0 error:error];
146 | if (!fileData) {
147 | return nil;
148 | }
149 |
150 | NSDictionary *projectPlist = [NSPropertyListSerialization propertyListWithData:fileData options:NSPropertyListImmutable format:NULL error:error];
151 | if (!projectPlist) {
152 | return nil;
153 | }
154 |
155 | // Get object version
156 | NSString *objectVersion = projectPlist[@"objectVersion"];
157 |
158 | // Get root object (project)
159 | self.objects = projectPlist[@"objects"];
160 | NSDictionary *rootObject = self.objects[projectPlist[@"rootObject"]];
161 |
162 | // Check compatibility version
163 | NSString *compatibilityVersion = rootObject[@"compatibilityVersion"];
164 |
165 | if (![XcodeCompatibilityVersionStringSet() containsObject:compatibilityVersion] && ![XcodeObjectVersionStringSet() containsObject:objectVersion]){
166 | if (error) {
167 | *error = [NSError errorForUnsupportedProjectURL:projectWrapperURL fileVersion:compatibilityVersion];
168 | }
169 | return nil;
170 | }
171 |
172 | // Get project targets
173 | NSArray *targets = [self objectArrayForDictionary:rootObject key:@"targets"];
174 |
175 | // Validate project config name to guard against name conflicts with target names
176 | NSError *nameValdationError = nil;
177 | NSArray *targetNames = [targets valueForKeyPath:@"name"];
178 | self.validatedProjectConfigName = [self validatedProjectConfigNameWithTargetNames:targetNames error: &nameValdationError];
179 |
180 | if (nameValdationError) {
181 | [nonFatalErrors addObject:nameValdationError];
182 | }
183 |
184 | // Get project settings
185 | NSString *buildConfigurationListID = rootObject[@"buildConfigurationList"];
186 | NSDictionary *projectSettings = [self buildSettingStringsByConfigurationForBuildConfigurationListID:buildConfigurationListID];
187 |
188 | self.buildSettingsByTarget[self.validatedProjectConfigName] = projectSettings;
189 |
190 | // Begin check that the project file has some settings
191 | BOOL projectFileHasSettings = projectSettings.tps_containsBuildSettings;
192 |
193 | // Add project targets
194 | for (NSDictionary *target in targets) {
195 | NSString *targetName = target[@"name"];
196 | buildConfigurationListID = target[@"buildConfigurationList"];
197 | NSDictionary *targetSettings = [self buildSettingStringsByConfigurationForBuildConfigurationListID:buildConfigurationListID];
198 | if (!projectFileHasSettings) { projectFileHasSettings = targetSettings.tps_containsBuildSettings; }
199 |
200 | self.buildSettingsByTarget[targetName] = targetSettings;
201 |
202 | }
203 |
204 | if (!projectFileHasSettings) {
205 | if (error) {
206 | *error = [NSError errorForNoSettingsFoundInProject: [projectWrapperURL lastPathComponent]];
207 | }
208 | return nil;
209 | }
210 |
211 |
212 | self.extractionSuccessful = YES;
213 | return nonFatalErrors;
214 | }
215 |
216 | // This will return a validated project config name to guard against naming conflicts with targets
217 | // If a conflict is found, the project config files will have "-Project-Settings" appended to the
218 | // provided project config name, using the user-specified name separator between words.
219 | - (NSString *)validatedProjectConfigNameWithTargetNames:(NSArray *)targetNames error:(NSError **)error {
220 | NSString *validatedProjectConfigName = self.projectConfigName;
221 | if ([targetNames containsObject:self.projectConfigName]) {
222 | validatedProjectConfigName = [validatedProjectConfigName stringByAppendingFormat:@"%@Project%@Settings", self.nameSeparator, self.nameSeparator];
223 | if (error) {
224 | *error = [NSError errorForNameConflictWithName:self.projectConfigName validatedName:validatedProjectConfigName];
225 | }
226 | }
227 | return validatedProjectConfigName;
228 | }
229 |
230 | /* Writes an xcconfig file for each target / configuration combination to the specified directory.
231 | */
232 | - (BOOL)writeConfigFilesToDestinationFolder:(NSURL *)destinationURL error:(NSError **)error {
233 |
234 | // It is a programming error to try to write before extracting or to call this method after extracting build settings has failed.
235 | if (!self.extractionSuccessful) {
236 | [NSException raise:NSInternalInconsistencyException format:@"The method -writeConfigFilesToDestinationFolder:error: was called before successful completion of -extractBuildSettingsFromProject:error:. Callers of this method must call -extractBuildSettingsFromProject:error: first and check for a non-nil return value indicating success."];
237 | }
238 |
239 | __block BOOL success = YES;
240 | __block NSError *blockError = nil;
241 |
242 | [self.buildSettingsByTarget enumerateKeysAndObjectsUsingBlock:^(id targetName, id obj, BOOL *stop) {
243 | [obj enumerateKeysAndObjectsUsingBlock:^(id configName, id settings, BOOL *stop) {
244 |
245 | NSString *filename = [self configFilenameWithTargetName:targetName configName:configName];
246 |
247 | NSString *configFileString = @"";
248 |
249 | // Add header comment
250 | NSString *headerComment = [self headerCommentForFilename:filename];
251 | if (headerComment) {
252 | configFileString = [configFileString stringByAppendingString:headerComment];
253 | }
254 |
255 | // If the config name is not the shared config, we need to import the shared config
256 | if (![configName isEqualToString:self.sharedConfigName]) {
257 | NSString *configFilename = [self configFilenameWithTargetName:targetName configName:self.sharedConfigName];
258 | NSString *includeDirective = [NSString stringWithFormat:@"\n\n#include \"%@\"", configFilename];
259 | configFileString = [configFileString stringByAppendingString:includeDirective];
260 | }
261 |
262 | // If there are no settings at all, add a comment that the lack of settings is on purpose
263 | if ([settings isEqualToString:@""]) {
264 | settings = [settings stringByAppendingString:@"\n\n"];
265 | settings = [settings stringByAppendingString:@"//********************************************//\n"];
266 | settings = [settings stringByAppendingString:@"//* Currently no build settings in this file *//\n"];
267 | settings = [settings stringByAppendingString:@"//********************************************//"];
268 |
269 | ;
270 | } else {
271 | // Add a few lines before first setting
272 | configFileString = [configFileString stringByAppendingString:@"\n\n"];
273 | }
274 |
275 | configFileString = [configFileString stringByAppendingString:settings];
276 |
277 | // Trim whitespace and newlines
278 | configFileString = [configFileString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
279 |
280 | // Add a newline at end of file
281 | configFileString = [configFileString stringByAppendingString:@"\n"];
282 |
283 | BOOL isProcessingProjectConfigFiles = [targetName isEqualToString:self.validatedProjectConfigName];
284 | BOOL currentTargetCanUseFolder = isProcessingProjectConfigFiles ? self.projectFolderEnabled : YES;
285 |
286 | NSURL *fileURL = nil;
287 | if (self.targetFoldersEnabled && currentTargetCanUseFolder) {
288 | fileURL = [destinationURL URLByAppendingPathComponent:targetName];
289 | success = [[NSFileManager defaultManager] createDirectoryAtURL:fileURL withIntermediateDirectories:YES attributes:nil error:&blockError];
290 | fileURL = [fileURL URLByAppendingPathComponent:filename];
291 | } else {
292 | fileURL = [destinationURL URLByAppendingPathComponent:filename];
293 | }
294 |
295 | // Don't write to file if directory creation failed
296 | if (success) {
297 | success = [configFileString writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:&blockError];
298 | }
299 | // If we failed to write a configuration, stop iterating through this target's configurations
300 | if (!success) { *stop = YES; }
301 | }];
302 |
303 | // If we failed writing a target's configurations, stop iterating through targets
304 | if (!success) { *stop = YES; }
305 | }];
306 |
307 | if (!success && error) {
308 | *error = blockError;
309 | }
310 |
311 | return success;
312 | }
313 |
314 | // Given the target name and config name returns the xcconfig filename to be used.
315 | - (NSString *)configFilenameWithTargetName:(NSString *)targetName configName:(NSString *)configName {
316 |
317 | // Use empty separator if there is no config name
318 | NSString *separator = configName.length ? self.nameSeparator: @"";
319 |
320 | NSString *filename = [NSString stringWithFormat:@"%@%@%@.xcconfig", targetName, separator, configName];
321 |
322 | // Replace all spaces in filename with separator.
323 | filename = [filename stringByReplacingOccurrencesOfString:@" " withString:self.nameSeparator];
324 |
325 | return filename;
326 | }
327 |
328 | // Given the filename generate the header comment
329 | - (NSString *)headerCommentForFilename:(NSString *)filename {
330 | NSString *headerComment = @"";
331 |
332 | NSString *dateString = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle];
333 |
334 | headerComment = [headerComment stringByAppendingString:@"//\n"];
335 | headerComment = [headerComment stringByAppendingFormat:@"// %@\n", filename];
336 | headerComment = [headerComment stringByAppendingString:@"//\n"];
337 | headerComment = [headerComment stringByAppendingFormat:@"// Generated by BuildSettingExtractor on %@\n", dateString];
338 | headerComment = [headerComment stringByAppendingString:@"// https://buildsettingextractor.com\n"];
339 | headerComment = [headerComment stringByAppendingString:@"//"];
340 |
341 | return headerComment;
342 | }
343 |
344 |
345 | /* Given a build setting dictionary, returns a string representation of the build settings, suitable for an xcconfig file. */
346 | - (NSString *)stringRepresentationOfBuildSettings:(NSDictionary *)buildSettings {
347 | return [BuildSettingExtractor stringRepresentationOfBuildSettings:buildSettings includeBuildSettingInfoComments:self.includeBuildSettingInfoComments alignBuildSettingValues:self.alignBuildSettingValues linesBetweenSettings:self.linesBetweenSettings commentGenerator:self.buildSettingCommentGenerator];
348 | }
349 |
350 | /* Given a build setting dictionary and a set of options, returns a string representation of the build settings, suitable for an xcconfig file. Logic is shared between generating xcconfig files and providing a formatting example. */
351 | + (NSString *)stringRepresentationOfBuildSettings:(NSDictionary *)buildSettings includeBuildSettingInfoComments:(BOOL)includeBuildSettingInfoComments alignBuildSettingValues:(BOOL)alignBuildSettingValues linesBetweenSettings:(NSInteger)linesBetweenSettings commentGenerator:(BuildSettingCommentGenerator *) buildSettingCommentGenerator {
352 | NSMutableString *string = [[NSMutableString alloc] init];
353 |
354 | // Sort build settings by name for easier reading and testing. Case insensitive compare should stay stable regardess of locale.
355 | NSArray *sortedKeys = [[buildSettings allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
356 |
357 | NSUInteger maxKeyLength = 0;
358 | if (alignBuildSettingValues) {
359 | for (NSString *key in sortedKeys) {
360 | maxKeyLength = MAX(maxKeyLength, key.length);
361 | }
362 | }
363 |
364 | BOOL firstKey = YES;
365 | NSString *previousKey = nil;
366 | for (NSString *key in sortedKeys) {
367 | id value = buildSettings[key];
368 |
369 | // If same base setting name as previous key, this is a conditional build setting.
370 | // Don't put newlines between them and don't repeat the build setting info comment.
371 | BOOL sameBaseSettingName = [previousKey tps_baseBuildSettingNameIsEqualTo:key]; // nil previousKey returns nil aka NO
372 |
373 | if (!firstKey && !sameBaseSettingName){
374 | for (NSInteger i = 0; i < linesBetweenSettings; i++) {
375 | [string appendString:@"\n"];
376 | }
377 | }
378 |
379 | if (includeBuildSettingInfoComments && !sameBaseSettingName) {
380 | NSString *comment = [buildSettingCommentGenerator commentForBuildSettingWithName:key];
381 | [string appendString:comment];
382 | }
383 |
384 | [string appendString:key];
385 | if (alignBuildSettingValues) {
386 | for (NSUInteger currentLength = key.length; currentLength < maxKeyLength; currentLength++) {
387 | [string appendString:@" "];
388 | }
389 | }
390 |
391 | if ([value isKindOfClass:[NSString class]]) {
392 | [string appendFormat:@" = %@\n", value];
393 |
394 | } else if ([value isKindOfClass:[NSArray class]]) {
395 | [string appendFormat:@" = %@\n", [value componentsJoinedByString:@" "]];
396 | } else {
397 | [NSException raise:@"Should not get here!" format:@"Unexpected class: %@ in %s", [value class], __PRETTY_FUNCTION__];
398 | }
399 |
400 | previousKey = key;
401 | firstKey = NO;
402 | }
403 |
404 | return string;
405 | }
406 |
407 | /* Given a build configuration list ID, retrieves the list of build configurations, consolidates shared build settings into a shared configuration and returns a dictionary of build settings configurations as strings, keyed by configuration name. */
408 | - (NSDictionary *)buildSettingStringsByConfigurationForBuildConfigurationListID:(NSString *)buildConfigurationListID {
409 |
410 | // Get the array of build configuration objects for the build configuration list ID
411 | NSDictionary *buildConfigurationList = self.objects[buildConfigurationListID];
412 | NSArray *projectBuildConfigurations = [self objectArrayForDictionary:buildConfigurationList key:@"buildConfigurations"];
413 |
414 |
415 | NSDictionary *buildSettingsByConfiguration = [self buildSettingsByConfigurationForConfigurations:projectBuildConfigurations];
416 |
417 | // Turn each build setting into a build setting string. Store by configuration name
418 | NSMutableDictionary *buildSettingStringsByConfiguration = [[NSMutableDictionary alloc] init];
419 | [buildSettingsByConfiguration enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
420 | NSString *buildSettingsString = [self stringRepresentationOfBuildSettings:obj];
421 | [buildSettingStringsByConfiguration setValue:buildSettingsString forKey:key];
422 |
423 | }];
424 | return buildSettingStringsByConfiguration;
425 |
426 | }
427 |
428 |
429 | /* Given an array of build configuration dictionaries, removes common build settings into a shared build configuration and returns a dictionary of build settings dictionaries, keyed by configuration name.
430 | */
431 | - (NSDictionary *)buildSettingsByConfigurationForConfigurations:(NSArray *)buildConfigurations {
432 |
433 | NSMutableDictionary *buildSettingsByConfiguration = [[NSMutableDictionary alloc] init];
434 |
435 | NSMutableDictionary *sharedBuildSettings = [[NSMutableDictionary alloc] init];
436 | NSDictionary *firstBuildSettings = nil;
437 | NSInteger index = 0;
438 |
439 | for (NSDictionary *buildConfiguration in buildConfigurations) {
440 |
441 | NSDictionary *buildSettings = buildConfiguration[@"buildSettings"];
442 |
443 | // Use first build settings as a starting point, represents all settings after first iteration
444 | if (index == 0) {
445 | firstBuildSettings = buildSettings;
446 |
447 | }
448 |
449 | // Second iteration, compare second against first build settings to come up with common items
450 | else if (index == 1){
451 | [firstBuildSettings enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
452 | id otherObj = buildSettings[key];
453 | if ([obj isEqualTo:otherObj]) {
454 | sharedBuildSettings[key] = obj;
455 | }
456 | }];
457 | }
458 |
459 | // Subsequent iteratons, remove common items that don't match current config settings
460 | else {
461 | [[sharedBuildSettings copy] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
462 | id otherObj = buildSettings[key];
463 | if (![obj isEqualTo:otherObj]) {
464 | [sharedBuildSettings removeObjectForKey:key];
465 | }
466 | }];
467 | }
468 |
469 | index++;
470 | }
471 |
472 | [buildSettingsByConfiguration setValue:sharedBuildSettings forKey:self.sharedConfigName];
473 |
474 | NSArray *sharedKeys = [sharedBuildSettings allKeys];
475 | for (NSDictionary *projectBuildConfiguration in buildConfigurations) {
476 | NSString *configName = projectBuildConfiguration[@"name"];
477 | NSMutableDictionary *buildSettings = projectBuildConfiguration[@"buildSettings"];
478 | [buildSettings removeObjectsForKeys:sharedKeys];
479 | [buildSettingsByConfiguration setValue:buildSettings forKey:configName];
480 |
481 | }
482 |
483 | return buildSettingsByConfiguration;
484 | }
485 |
486 | + (NSString *)exampleBuildFormattingStringForSettings:(NSDictionary *)settings includeBuildSettingInfoComments:(BOOL)includeBuildSettingInfoComments alignBuildSettingValues:(BOOL)alignBuildSettingValues linesBetweenSettings:(NSInteger)linesBetweenSettings {
487 |
488 | BuildSettingCommentGenerator *buildSettingCommentGenerator = nil;
489 |
490 | if (includeBuildSettingInfoComments) {
491 |
492 | NSError *infoSourceError = nil;
493 | BuildSettingInfoSource *infoSource = [BuildSettingInfoSource resolvedBuildSettingInfoSourceWithStyle:BuildSettingInfoSourceStyleStandard customURL:nil error:&infoSourceError];
494 |
495 | if (infoSource) {
496 | buildSettingCommentGenerator = [[BuildSettingCommentGenerator alloc] initWithBuildSettingInfoSource:infoSource];
497 | }
498 | }
499 |
500 | return [BuildSettingExtractor stringRepresentationOfBuildSettings:settings includeBuildSettingInfoComments:includeBuildSettingInfoComments alignBuildSettingValues:alignBuildSettingValues linesBetweenSettings:linesBetweenSettings commentGenerator:buildSettingCommentGenerator];
501 | }
502 |
503 | @end
504 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingInfoSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingInfoSource.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 10/9/19.
6 | // Copyright © 2019 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, BuildSettingInfoSourceStyle) {
12 | BuildSettingInfoSourceStyleStandard = 100,
13 | };
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface BuildSettingInfoSource : NSObject
18 |
19 | @property (nullable) NSURL *resolvedURL; // nil if unresolved
20 | @property NSInteger resolvedVersion; // -1 if unresolved
21 |
22 | // On success, will return a BuildSettingInfoSource instance with valid values for resolvedURL and resolvedVersion.
23 | //
24 | // Even on success, error value may be present if resolved value was not from the expected source.
25 | // For example, if a custom URL was provided but not found the resolved info source may be the version of Xcode
26 | // in its standard location.
27 | //
28 | // On failure will return nil and an error if unable to resolve a build setting info source.
29 |
30 | + (nullable BuildSettingInfoSource *)resolvedBuildSettingInfoSourceWithStyle:(BuildSettingInfoSourceStyle)style customURL:(nullable NSURL *)customURL error:(NSError **)error;
31 |
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingInfoSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingInfoSource.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 10/9/19.
6 | // Copyright © 2019 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "BuildSettingInfoSource.h"
10 | #import "Constants+Categories.h"
11 |
12 | @interface BuildSettingInfoSource ()
13 | @property BuildSettingInfoSourceStyle style;
14 | @property (nullable) NSURL *customURL;
15 | @property NSString *standardAppPath;
16 | @property NSString *standardBetaPath;
17 | @end
18 |
19 | @implementation BuildSettingInfoSource
20 |
21 | + (BuildSettingInfoSource *)resolvedBuildSettingInfoSourceWithStyle:(BuildSettingInfoSourceStyle)style customURL:(NSURL *)customURL error:(NSError **)error {
22 |
23 | BuildSettingInfoSource *infoSource = [[BuildSettingInfoSource alloc] initWithStyle:style customURL:customURL];
24 | BOOL success = [infoSource resolveBuildSettingInfoSourceWithError:error];
25 |
26 | if (success) {
27 | return infoSource;
28 | } else {
29 | return nil;
30 | }
31 | }
32 |
33 | - (instancetype)init {
34 | return [self initWithStyle:BuildSettingInfoSourceStyleStandard customURL:nil];
35 | }
36 |
37 | - (instancetype)initWithStyle:(BuildSettingInfoSourceStyle)style customURL:(nullable NSURL *)customURL {
38 | self = [super init];
39 | if (self) {
40 | self.style = style;
41 | self.customURL = customURL;
42 | self.resolvedURL = nil;
43 | self.resolvedVersion = -1;
44 | self.standardAppPath = @"/Applications/Xcode.app";
45 | self.standardBetaPath = @"/Applications/Xcode-beta.app";
46 |
47 | // If using the special style for testing failures, set incorrect paths
48 | if (style == -1) {
49 | self.standardAppPath = @"/Supplications/EXcode.app";
50 | self.standardBetaPath = @"/Supplications/EXcode-beta.app";
51 | }
52 | }
53 | return self;
54 | }
55 |
56 | - (BOOL)resolveBuildSettingInfoSourceWithError: (NSError **)error {
57 | BOOL successfullyResolved = NO;
58 |
59 | BOOL standardAppExists = [[NSFileManager defaultManager] fileExistsAtPath:self.standardAppPath];
60 | NSInteger standardAppVersion = -1;
61 | NSURL *standardAppURL = nil;
62 |
63 | BOOL standardBetaExists = [[NSFileManager defaultManager] fileExistsAtPath:self.standardBetaPath];
64 | NSInteger standardBetaVersion = -1;
65 | NSURL *standardBetaURL = nil;
66 |
67 | if (standardAppExists) {
68 | standardAppURL = [NSURL fileURLWithPath:self.standardAppPath];
69 | standardAppVersion = [self versionForXcodeAtURL:standardAppURL];
70 | }
71 |
72 | if (standardBetaExists) {
73 | standardBetaURL = [NSURL fileURLWithPath:self.standardBetaPath];
74 | standardBetaVersion = [self versionForXcodeAtURL:standardBetaURL];
75 | }
76 |
77 | // If both are found use the beta if it is a later version
78 | if (standardAppExists && standardBetaExists) {
79 | if (standardBetaVersion > standardAppVersion) {
80 | self.resolvedURL = standardBetaURL;
81 | self.resolvedVersion = standardBetaVersion;
82 | } else {
83 | self.resolvedURL = standardAppURL;
84 | self.resolvedVersion = standardAppVersion;
85 | }
86 | }
87 |
88 | else if (standardAppExists) {
89 | self.resolvedURL = standardAppURL;
90 | self.resolvedVersion = standardAppVersion;
91 | }
92 |
93 | else if (standardBetaExists) {
94 | self.resolvedURL = standardBetaURL;
95 | self.resolvedVersion = standardBetaVersion;
96 | }
97 |
98 | if (self.resolvedURL != nil && self.resolvedVersion != -1) {
99 | successfullyResolved = YES;
100 | } else {
101 | if (error) {
102 | *error = [NSError errorForUnresolvedBuildSettingInfoSource];
103 | }
104 | }
105 |
106 | return successfullyResolved;
107 | }
108 |
109 | - (NSInteger)versionForXcodeAtURL:(NSURL *)url {
110 | NSString *pathToXcodeInfoPlist = [[url path] stringByAppendingPathComponent:@"Contents/Info.plist"];
111 | NSDictionary *xcodeInfoDictionary = [NSDictionary dictionaryWithContentsOfFile:pathToXcodeInfoPlist];
112 | NSString *versionString = xcodeInfoDictionary[@"DTXcode"];
113 | NSInteger xcodeVersion = [versionString integerValue];
114 | if (!versionString || xcodeVersion == 0) {
115 | NSLog(@"Could not read Xcode version. Version string: %@, Version Number: %ld", versionString, (long)xcodeVersion);
116 | }
117 | return xcodeVersion;
118 | }
119 |
120 | @end
121 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingInfoSubpaths.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | backstopSettingInfo
6 |
7 | [COMBINE_HIDPI_IMAGES]-description
8 | Combines image files at different resolutions into one multi-page TIFF file that is HiDPI compliant for macOS 10.7 and later. Only image files in the same directory and with the same base name and extension are combined. The file names must conform to the naming convention used in HiDPI.
9 | [COMBINE_HIDPI_IMAGES]-name
10 | Combine HiDPI Images
11 | [IPHONEOS_DEPLOYMENT_TARGET]-description
12 | Code will load on this and later versions of iOS. Framework APIs that are unavailable in earlier versions will be weak-linked; your code should check for null function pointers or specific system versions before calling newer APIs.
13 | [IPHONEOS_DEPLOYMENT_TARGET]-name
14 | iOS Deployment Target
15 | [MARKETING_VERSION]-description
16 | This setting defines the user-visible version of the project. The value corresponds to the `CFBundleShortVersionString` key in your app's Info.plist.
17 | [MARKETING_VERSION]-name
18 | Marketing Version
19 | [SWIFT_COMPILATION_MODE]-description
20 | This setting controls the way the Swift files in a module are rebuilt.\n- Incremental: Only rebuild the Swift source files in the module that are out of date, running multiple compiler processes as needed.\n- Whole Module: Always rebuild all Swift source files in the module, in a single compiler process.
21 | [SWIFT_COMPILATION_MODE]-name
22 | Swift Compilation Mode
23 | [SWIFT_OPTIMIZATION_LEVEL]-description
24 | None [-Onone], Fastest [-O], Fastest, Unchecked [-Ounchecked]
25 | [SWIFT_OPTIMIZATION_LEVEL]-name
26 | Swift Optimization Level
27 | [TVOS_DEPLOYMENT_TARGET]-description
28 | Code will load on this and later versions of tvOS. Framework APIs that are unavailable in earlier versions will be weak-linked; your code should check for null function pointers or specific system versions before calling newer APIs.
29 | [TVOS_DEPLOYMENT_TARGET]-name
30 | tvOS Deployment Target
31 | [WATCHOS_DEPLOYMENT_TARGET]-description
32 | Code will load on this and later versions of watchOS. Framework APIs that are unavailable in earlier versions will be weak-linked; your code should check for null function pointers or specific system versions before calling newer APIs.
33 | [WATCHOS_DEPLOYMENT_TARGET]-name
34 | watchOS Deployment Target
35 | [SWIFT_VERSION]-name
36 | Swift Language Version
37 | [SWIFT_VERSION]-description
38 | Interpret input according to a specific Swift language version number.
39 |
40 | deprecatedSubpathsByVersion
41 |
42 | 1200
43 |
44 |
45 | Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/Native Build System.strings
46 | Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/Native Build System.xcspec
47 |
48 |
49 | 0900
50 |
51 |
52 | Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Specifications/English.lproj/Native Build System.strings
53 |
54 |
55 |
56 | introducedSubpathsByVersion
57 |
58 | 1200
59 |
60 |
61 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Metal.xcplugin/Contents/Resources/MetalLinker.strings
62 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Metal.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.metal-linker.strings
63 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.metal-linker.strings
64 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.metal-linker.strings
65 |
66 |
67 |
68 | subpaths
69 |
70 |
71 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/Frameworks/DevToolsCore.framework/Versions/Current/Resources/en.lproj/CoreBuildSystem.strings
72 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/Frameworks/DevToolsCore.framework/Versions/Current/Resources/English.lproj/CoreBuildSystem.strings
73 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.strings
74 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.strings
75 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.strings
76 |
77 |
78 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 6.0.strings
79 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 6.1.strings
80 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 7.0.strings
81 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 7.1.strings
82 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 8.0.strings
83 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 8.1.strings
84 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple LLVM 9.0.strings
85 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/English.lproj/Apple Clang.strings
86 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Clang LLVM 1.0.xcplugin/Contents/Resources/en.lproj/Apple Clang.strings
87 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Apple Clang.strings
88 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Apple Clang.strings
89 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Apple Clang.strings
90 |
91 |
92 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/XCLanguageSupport.xcplugin/Contents/Resources/en.lproj/Swift Compiler.strings
93 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Swift Compiler.strings
94 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Swift Compiler.strings
95 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Swift Compiler.strings
96 |
97 |
98 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/XCLanguageSupport.xcplugin/Contents/Resources/SwiftBuildSettings.strings
99 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/SwiftBuildSettings.strings
100 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/SwiftBuildSettings.strings
101 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/SwiftBuildSettings.strings
102 |
103 |
104 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Metal.xcplugin/Contents/Resources/Metal Compiler.strings
105 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Metal.xcplugin/Contents/Resources/MetalCompiler.strings
106 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Metal.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.metal.strings
107 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.metal.strings
108 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.metal.strings
109 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.metal.strings
110 |
111 |
112 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.model.coredata.strings
113 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/English.lproj/com.apple.compilers.model.coredata.strings
114 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredata.strings
115 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredata.strings
116 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredata.strings
117 |
118 |
119 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.model.coredatamapping.strings
120 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/English.lproj/com.apple.compilers.model.coredatamapping.strings
121 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredatamapping.strings
122 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredatamapping.strings
123 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.coredatamapping.strings
124 |
125 |
126 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.model.persistence.strings
127 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/Core Data.xcplugin/Contents/Resources/English.lproj/com.apple.compilers.model.persistence.strings
128 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.persistence.strings
129 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.persistence.strings
130 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.model.persistence.strings
131 |
132 |
133 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/AssetCatalogCompiler.xcspec
134 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/AssetCatalogCompiler.xcspec
135 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/AssetCatalogCompiler.xcspec
136 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/AssetCatalogCompiler.xcspec
137 |
138 |
139 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/en.lproj/com.apple.compilers.assetcatalog.strings
140 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/English.lproj/com.apple.compilers.assetcatalog.strings
141 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.assetcatalog.strings
142 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.assetcatalog.strings
143 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.compilers.assetcatalog.strings
144 |
145 |
146 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/en.lproj/com.apple.xcode.tools.ibtool.compiler.strings
147 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/English.lproj/com.apple.xcode.tools.ibtool.compiler.strings
148 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.compiler.strings
149 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.compiler.strings
150 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.compiler.strings
151 |
152 |
153 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/en.lproj/com.apple.xcode.tools.ibtool.postprocessor.strings
154 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/English.lproj/com.apple.xcode.tools.ibtool.postprocessor.strings
155 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.postprocessor.strings
156 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.postprocessor.strings
157 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/com.apple.xcode.tools.ibtool.postprocessor.strings
158 |
159 |
160 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/en.lproj/Interface Builder Storyboard Compiler.strings
161 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/English.lproj/Interface Builder Storyboard Compiler.strings
162 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Compiler.strings
163 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Compiler.strings
164 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Compiler.strings
165 |
166 |
167 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/en.lproj/Interface Builder Storyboard Postprocessor.strings
168 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/IBCompilerPlugin.xcplugin/Contents/Resources/English.lproj/Interface Builder Storyboard Postprocessor.strings
169 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Postprocessor.strings
170 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Postprocessor.strings
171 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Interface Builder Storyboard Postprocessor.strings
172 |
173 |
174 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/CoreBuildTasks.xcplugin/Contents/Resources/en.lproj/OpenCL.strings
175 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/CoreBuildTasks.xcplugin/Contents/Resources/English.lproj/OpenCL.strings
176 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/OpenCL.strings
177 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/OpenCL.strings
178 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/OpenCL.strings
179 |
180 |
181 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/Frameworks/DevToolsCore.framework/Versions/A/Resources/CoreBuildSystem.xcspec
182 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.xcspec
183 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.xcspec
184 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/CoreBuildSystem.xcspec
185 |
186 |
187 | Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/PrivatePlugIns/IDEiOSPlatformSupportCore.ideplugin/Contents/Resources/Device.xcspec
188 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Embedded-Shared.xcspec
189 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Embedded-Shared.xcspec
190 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Embedded-Shared.xcspec
191 |
192 |
193 | Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins/XCLanguageSupport.xcplugin/Contents/Resources/Swift.xcspec
194 | Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Swift.xcspec
195 | Contents/Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Swift.xcspec
196 | Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Swift.xcspec
197 |
198 |
199 |
200 |
201 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/BuildSettingInfoSubpathsNotes.md:
--------------------------------------------------------------------------------
1 | # BuildSettingInfoSubpaths
2 |
3 | An overview of the structure and keys in the BuildSettingInfoSubpaths.plist file.
4 |
5 | ## Overview
6 | BuildSettingExtractor gleans information about build settings from files within the Xcode app bundle. The `BuildSettingInfoSubpaths.plist` file is a listing of the paths to those files. All paths in the plist file are relative to the `Xcode.app` app wrapper.
7 |
8 | The build setting info is sometimes contained in `.strings` files and sometimes in `.xcspec` files.
9 |
10 | ## Arrays of subpaths
11 | From release to release of Xcode, sometimes the same logical file will have a new path or file name. For instance, many had _English.lproj_ as part of the path, which was changed to _en.lproj_ in subsequent releases. More recently, file names have been gradually being renamed to use a reverse-DNS naming style.
12 |
13 | Rather than keep detailed versioning bookkeeping, each array contains a list of paths, at least one of which should be found. If what is conceptually the same file has a different path or file name in a new release, the new path should be added to the array. Ideally it should be added to the beginning of the array, since it will be found sooner when searching more recent Xcode versions. In practice, these arrays are so short the difference is negligible.
14 |
15 | ## Keys
16 | ### subpaths
17 | #### An array of arrays of subpath strings
18 | Each item is an array of paths representing one logical file containing build setting info. The assumption is that at least one of these paths is valid across all versions of Xcode.
19 |
20 | Add an entry to a subpath array in this key if the path or filename of an existing entry has changed in a new version of Xcode. Theoretically, any new file containing build setting info would be added to the _ introducedSubpathsByVersion_ key since the file does not exist in prior versions of Xcode.
21 |
22 | ### deprecatedSubpathsByVersion
23 | #### A dictionary with Xcode version as the key and an array of arrays of subpath strings as the value.
24 | Each key is a version string matching the version string found for the key `DTXcode` in the Xcode `Info.plist` file. The version number matches the version the file was first removed.
25 |
26 | The value for each key is an array that works in the same manner as an entry in the subpaths array.
27 |
28 | Add to this key when a build setting info file is removed completely. This prevents a warning / error / test failure when using newer Xcode versions as the build info source, but still allows the file to be successfully included when using older Xcode versions as the build info source.
29 |
30 | ### introducedSubpathsByVersion
31 | #### A dictionary with Xcode version as the key and an array of arrays of subpath strings as the value.
32 | Each key is a version string matching the version string found for the key `DTXcode` the Xcode `Info.plist` file. The version number matches the version the file first appeared.
33 |
34 | The value for each key is an array that works in the same manner as an entry in the subpaths array.
35 |
36 | Add to this key when a completely new build setting info file is added. This prevents a warning / error / test failure when using older Xcode versions as the build info source, but allows the file to be successfully included when using newer Xcode versions as the build info source.
37 |
38 | Note that, just as with the subpaths values, any subpath introduced here is represented as an array. If the path or filename changes for the same logical file, the new subpath should be added to the array.
39 |
40 | ### backstopSettingInfo
41 | #### A dictionary with string keys and values
42 | Sometimes Xcode does not have a title or description defined for a build setting, or alternately, the file that provides that information has been elusive.
43 |
44 | This dictionary provides that missing information as a backstop in case the information is not found by searching the build info files specified by the other keys.
45 |
46 | The keys take the form `\[_BuildSettingName_\]-name` for the human-readable title of the build setting and `\[_BuildSettingName_\]-description` for the description of the setting.
47 |
48 | The keys match the format of keys found in the various `.strings` files containing build setting info.
49 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Constants+Categories.h:
--------------------------------------------------------------------------------
1 | //
2 | // Constants+Categories.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | typedef NS_ENUM(NSUInteger, BuildSettingExtractorErrorCodes) {
12 | UnsupportedXcodeVersion = 100,
13 | DirectoryContainsBuildConfigFiles = 101,
14 | ProjectSettingsNamingConflict = 102,
15 | NoSettingsFoundInProjectFile = 103,
16 | BuildSettingInfoSourceNotFound = 104,
17 | BuildSettingInfoFilesNotFound = 105,
18 | BuildSettingInfoSubpathNotFound = 106,
19 | };
20 |
21 | extern NSErrorDomain const TPSBuildSettingExtractorErrorDomain;
22 | extern NSString * TPSMultipleUnderlyingErrorsKey(void);
23 |
24 | #pragma mark -
25 |
26 | @interface NSString (TPS_TypeIdentifierAdditions)
27 | + (NSString *)tps_projectBundleTypeIdentifier;
28 | + (NSString *)tps_buildConfigurationFileTypeIdentifier;
29 | + (NSString *)tps_preferredTypeIdentifierForFileExtension:(NSString *)string;
30 | @end
31 |
32 | @interface NSString (TPS_BuildSettingAdditions)
33 | - (NSString *)tps_baseBuildSettingName; // Removes any conditional section of a build setting
34 | - (BOOL)tps_baseBuildSettingNameIsEqualTo:(NSString *)buildSettingName; // returns YES if provided build setting name has the same base as the receiver
35 | @end
36 |
37 | #pragma mark -
38 |
39 | @interface NSDictionary (TPS_BuildSettingAdditions)
40 | // Assumes that a dictionary of build settings always has NSString values
41 | // Returns NO if all the values in a dictionary are an empty string
42 | // Raises an exception if used on a dictionary with any non-NSString value
43 | - (BOOL)tps_containsBuildSettings;
44 |
45 | // Returns a new dictionary containing all entries in the receiver except for
46 | // entries with an empty string value.
47 | - (NSDictionary *)tps_dictionaryByRemovingEmptyStringValues;
48 | @end
49 |
50 | #pragma mark -
51 |
52 | @interface NSError (TPS_BuildSettingExtractorAdditions)
53 |
54 | // Notify the user we are not using the exact name for the project settings provided in Preferences
55 | + (NSError *)errorForNameConflictWithName:(NSString *)conflictedName validatedName:(NSString *)validatedName;
56 |
57 | // Notify the user we did not find any settings in the project.
58 | + (NSError *)errorForNoSettingsFoundInProject:(NSString *)projectName;
59 |
60 | // Notify the user the project version is unsupported
61 | + (NSError *)errorForUnsupportedProjectURL:(NSURL *)projectWrapperURL fileVersion:(NSString *)compatibilityVersion;
62 |
63 | // Notify the user no build setting info source could was found
64 | + (NSError *)errorForUnresolvedBuildSettingInfoSource;
65 |
66 | // Notify the user the destination folder already contains build config files
67 | + (NSError *)errorForDestinationContainsBuildConfigFiles;
68 |
69 | // Error that one or more expected build setting info files were not found
70 | // User info includes NSMultipleUnderlyingErrorsKey to report underlying errors
71 | + (NSError *)errorForSettingInfoFilesNotFound:(NSArray *)subpathErrorStrings;
72 |
73 | @end
74 |
75 | #pragma mark -
76 |
77 | @interface EmptyStringTransformer: NSValueTransformer {}
78 | @end
79 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Constants+Categories.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSPasteboard+TPS_XcodeProjectReadingExtensions.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "Constants+Categories.h"
10 |
11 | NSErrorDomain const TPSBuildSettingExtractorErrorDomain = @"TPSBuildSettingExtractorErrorDomain";
12 |
13 | @implementation NSString (TPS_TypeIdentifierAdditions)
14 |
15 | + (NSString *)tps_projectBundleTypeIdentifier {
16 | static NSString *projectBundleTypeIdentifier;
17 | if (!projectBundleTypeIdentifier) {
18 | projectBundleTypeIdentifier = [self tps_preferredTypeIdentifierForFileExtension:@"xcodeproj"];
19 | // NSLog(@"Xcode UTI: %@", projectBundleTypeIdentifier);
20 | }
21 | return projectBundleTypeIdentifier;
22 | }
23 |
24 | + (NSString *)tps_buildConfigurationFileTypeIdentifier {
25 | static NSString *buildConfigurationFileTypeIdentifier;
26 | if (!buildConfigurationFileTypeIdentifier) {
27 | buildConfigurationFileTypeIdentifier = [self tps_preferredTypeIdentifierForFileExtension:@"xcconfig"];
28 | // NSLog(@"xcconfig UTI: %@", buildConfigurationFileTypeIdentifier);
29 | }
30 | return buildConfigurationFileTypeIdentifier;
31 | }
32 |
33 | + (NSString *)tps_preferredTypeIdentifierForFileExtension:(NSString *)string {
34 | NSString *identifier = CFBridgingRelease(UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)string, NULL));
35 | return identifier;
36 | }
37 |
38 | @end
39 |
40 | #pragma mark -
41 |
42 | @implementation NSString (TPS_BuildSettingAdditions)
43 |
44 | - (NSString *)tps_baseBuildSettingName {
45 | NSString *baseBuildSettingName = [self copy];
46 | NSRange range = [baseBuildSettingName rangeOfString:@"["]; // delimeter for a conditional build setting
47 | if (range.location != NSNotFound) {
48 | baseBuildSettingName = [baseBuildSettingName substringToIndex:range.location];
49 | }
50 | return baseBuildSettingName;
51 | }
52 |
53 | - (BOOL)tps_baseBuildSettingNameIsEqualTo:(NSString *)buildSettingName {
54 | return [[self tps_baseBuildSettingName] isEqualToString:[buildSettingName tps_baseBuildSettingName]];
55 | }
56 |
57 | @end
58 |
59 | #pragma mark -
60 |
61 | @implementation NSDictionary (TPS_BuildSettingAdditions)
62 |
63 | - (BOOL)tps_containsBuildSettings {
64 | BOOL foundNonEmptyString = NO;
65 | for (id value in self.allValues) {
66 | if ([value isKindOfClass:[NSString class]]) {
67 | if (![(NSString *)value isEqualToString:@""]) {
68 | foundNonEmptyString = YES;
69 | break;
70 | }
71 | } else {
72 | [NSException raise:(NSInternalInconsistencyException) format:@"-containsBuildSetting is expected to be called on a dictionary with NSString values."];
73 | }
74 | }
75 | return foundNonEmptyString;
76 | }
77 |
78 | - (NSDictionary *)tps_dictionaryByRemovingEmptyStringValues {
79 | NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
80 | [self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) {
81 | if (![value isEqualTo:@""]) {
82 | [temp setValue:value forKey:key];
83 | }
84 | }];
85 | return temp;
86 | }
87 |
88 | @end
89 |
90 | #pragma mark -
91 |
92 | @implementation NSError (TPS_BuildSettingExtractorAdditions)
93 |
94 | // Notify the user we are not using the exact name for the project settings provided in Preferences
95 | + (NSError *)errorForNameConflictWithName:(NSString *)conflictedName validatedName:(NSString *)validatedName {
96 | NSString *errorDescription = [NSString stringWithFormat:@"Project settings filename conflict."];
97 | NSString *errorRecoverySuggestion = [NSString stringWithFormat:@"The target \'%@\' has the same name as the project name set in Preferences.\n\nThe generated project settings files will use the name \'%@\' to avoid a conflict.", conflictedName, validatedName];
98 | NSDictionary *errorUserInfo = @{NSLocalizedDescriptionKey:errorDescription, NSLocalizedRecoverySuggestionErrorKey: errorRecoverySuggestion};
99 |
100 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:ProjectSettingsNamingConflict userInfo:errorUserInfo];
101 |
102 | return error;
103 | }
104 |
105 | // Notify the user we did not find any settings in the project.
106 | + (NSError *)errorForNoSettingsFoundInProject:(NSString *)projectName {
107 | NSString *errorDescription = [NSString stringWithFormat:@"No settings found."];
108 | NSString *errorRecoverySuggestion = [NSString stringWithFormat:@"No settings were found in the project \'%@\'.\n\nThe project may already be using .xcconfig files for its build settings.\n\nNo xcconfig files will be written. ", projectName];
109 | NSDictionary *errorUserInfo = @{NSLocalizedDescriptionKey:errorDescription, NSLocalizedRecoverySuggestionErrorKey: errorRecoverySuggestion};
110 |
111 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:NoSettingsFoundInProjectFile userInfo:errorUserInfo];
112 |
113 | return error;
114 | }
115 |
116 |
117 | + (NSError *)errorForUnsupportedProjectURL:(NSURL *)projectWrapperURL fileVersion:(NSString *)compatibilityVersion {
118 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Unable to extract build settings from project ‘%@’.", [[projectWrapperURL lastPathComponent] stringByDeletingPathExtension]], NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:@"Project file format version ‘%@’ is not supported.", compatibilityVersion]};
119 |
120 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:UnsupportedXcodeVersion userInfo:userInfo];
121 |
122 | return error;
123 | }
124 |
125 | + (NSError *)errorForUnresolvedBuildSettingInfoSource {
126 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey:@"No source of build setting info was found.", NSLocalizedRecoverySuggestionErrorKey:@"Xcode or Xcode-beta must be installed in the Applications folder to generate descriptive build setting comments.\n\nConfiguration files will be generated without comments."};
127 |
128 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:BuildSettingInfoSourceNotFound userInfo:userInfo];
129 |
130 | return error;
131 | }
132 |
133 | + (NSError *)errorForDestinationContainsBuildConfigFiles {
134 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey:@"Build config files already exist in this folder. Do you want to replace them?", NSLocalizedRecoveryOptionsErrorKey:@[@"Cancel", @"Replace"], NSLocalizedRecoverySuggestionErrorKey:@"Build configuration files already exist in this folder. Replacing will overwrite any files with the same file names."};
135 |
136 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:DirectoryContainsBuildConfigFiles userInfo:userInfo];
137 |
138 | return error;
139 | }
140 |
141 | + (NSError *)errorForSettingInfoFilesNotFound:(NSArray *)subpathErrorStrings {
142 | if (subpathErrorStrings.count == 0) {
143 | [NSException raise:NSInternalInconsistencyException format:@"Attempt to create BuildSettingInfoSubpathNotFound error with no underlying errors"];
144 | }
145 | NSMutableArray *underlyingErrors = [[NSMutableArray alloc] init];
146 | for (NSString *errorString in subpathErrorStrings) {
147 | NSDictionary *subpathErrorUserInfo = @{NSLocalizedDescriptionKey:errorString};
148 | NSError *subpathError = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:BuildSettingInfoSubpathNotFound userInfo:subpathErrorUserInfo];
149 | [underlyingErrors addObject:subpathError];
150 | }
151 |
152 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey:@"Some build info files not found.", TPSMultipleUnderlyingErrorsKey():underlyingErrors};
153 |
154 | NSError *error = [NSError errorWithDomain:TPSBuildSettingExtractorErrorDomain code:BuildSettingInfoFilesNotFound userInfo:userInfo];
155 |
156 | return error;
157 | }
158 |
159 | @end
160 |
161 | // Can remove once deployment target is macOS 11.3 or later
162 | NSString * TPSMultipleUnderlyingErrorsKey(void) {
163 | if (@available(macOS 11.3, *)) {
164 | return NSMultipleUnderlyingErrorsKey;
165 | } else {
166 | return @"NSMultipleUnderlyingErrorsKey";
167 | }
168 | }
169 |
170 | #pragma mark -
171 |
172 | @implementation EmptyStringTransformer
173 | + (Class)transformedValueClass { return [NSString class]; }
174 | + (BOOL)allowsReverseTransformation { return YES; }
175 |
176 | - (id)transformedValue:(id)value {
177 | if ([value isKindOfClass:[NSString class]] && [value isEqualToString:@""]) {
178 | return nil;
179 | } else if (value == nil) {
180 | return @"";
181 | } else {
182 | return value;
183 | }
184 | }
185 |
186 | - (id)reversedTransformedValue:(id)value {
187 | if (value == nil) { return @""; }
188 | else { return value; }
189 | }
190 | @end
191 |
192 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/DragFileView.h:
--------------------------------------------------------------------------------
1 | //
2 | // DragFileView.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Cocoa;
10 |
11 | IB_DESIGNABLE
12 | @interface DragFileView : NSBox
13 |
14 | @property (readonly) NSURL *fileURL;
15 |
16 | @property (weak) id target;
17 | @property SEL action;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/DragFileView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DragFileView.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 1/30/15.
6 | // Copyright (c) 2015 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "DragFileView.h"
10 | #import "AppConstants+Categories.h"
11 | #import "Constants+Categories.h"
12 |
13 |
14 | @interface DragFileView ()
15 | @property NSURL *fileURL;
16 | @property (weak) IBOutlet NSTextField *labelView;
17 | @end
18 |
19 | @implementation DragFileView
20 |
21 | - (void)commonInit {
22 | self.boxType = NSBoxCustom;
23 | self.cornerRadius = 20.0;
24 | self.borderWidth = 0.0;
25 | [self setHighlight:NO];
26 | [self registerForDraggedTypes:@[(NSString *)kUTTypeFileURL]];
27 | }
28 |
29 | - (instancetype)initWithCoder:(NSCoder *)coder {
30 | if (self = [super initWithCoder:coder]) { [self commonInit]; } return self;
31 | }
32 |
33 | - (instancetype)initWithFrame:(NSRect)frameRect {
34 | if (self = [super initWithFrame:frameRect]) { [self commonInit]; } return self;
35 | }
36 |
37 | - (void)setHighlight:(BOOL)flag {
38 | if (flag) {
39 | if (@available(macOS 10.14, *)) {
40 | self.fillColor = [[NSColor colorNamed: @"dragViewBackgroundColor"] colorWithSystemEffect:NSColorSystemEffectPressed];
41 | } else {
42 | self.fillColor = [NSColor colorWithCalibratedRed:0.56 green:0.7 blue:0.81 alpha:1.0];
43 | }
44 | } else {
45 | if (@available(macOS 10.13, *)) {
46 | self.fillColor = [NSColor colorNamed: @"dragViewBackgroundColor"];
47 | } else {
48 | self.fillColor = [NSColor colorWithCalibratedRed:0.7 green:0.85 blue:1.0 alpha:1.0];
49 | }
50 | }
51 | }
52 |
53 | - (NSDragOperation)draggingEntered:(id )sender {
54 |
55 | BOOL canRead = [[sender draggingPasteboard] tps_canReadXcodeProjectFileURL];
56 |
57 | if (canRead) {
58 | [self setHighlight:YES];
59 | return NSDragOperationGeneric;
60 | } else {
61 | return NSDragOperationNone;
62 | }
63 | }
64 |
65 | - (void)draggingExited:(id )sender {
66 | [self setHighlight:NO];
67 | }
68 |
69 | - (BOOL)prepareForDragOperation:(id )sender {
70 | return [[sender draggingPasteboard] tps_canReadXcodeProjectFileURL];
71 | }
72 |
73 | - (BOOL)performDragOperation:(id )sender {
74 | self.fileURL = [[sender draggingPasteboard] tps_readXcodeProjectFileURL];
75 | return self.fileURL != nil;
76 | }
77 |
78 | - (void)concludeDragOperation:(id )sender {
79 | [self setHighlight:NO];
80 | [NSApp sendAction:self.action to:self.target from:self];
81 | }
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/ContentsPreferencesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ContentsPreferencesViewController.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/14/20.
6 | // Copyright © 2020 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface ContentsPreferencesViewController : NSViewController
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/ContentsPreferencesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ContentsPreferencesViewController.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/14/20.
6 | // Copyright © 2020 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "ContentsPreferencesViewController.h"
10 | #import "AppConstants+Categories.h"
11 | #import "BuildSettingExtractor.h"
12 |
13 | @interface ContentsPreferencesViewController ()
14 | @property (unsafe_unretained) IBOutlet NSTextView *exampleOutputTextView;
15 | @property (weak) IBOutlet NSTextField *linesBetweenSettingsTextField;
16 | @property (weak) IBOutlet NSStepper *linesBetweenSettingsStepper;
17 | @property (weak) IBOutlet NSTextField *linesBetweenSettingsLabel;
18 | @end
19 |
20 | @implementation ContentsPreferencesViewController
21 |
22 | - (NSSize)preferredContentSize {
23 | return NSMakeSize(744.0, 352.0);
24 | }
25 |
26 | - (void)viewDidLoad {
27 | [super viewDidLoad];
28 | NSArray *defaultKeys = @[TPSIncludeBuildSettingInfoComments, TPSLinesBetweenBuildSettingsWithInfo, TPSLinesBetweenBuildSettings, TPSAlignBuildSettingValues];
29 | for (NSString *key in defaultKeys) {
30 | [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:key options:0 context:nil];
31 | }
32 | }
33 |
34 | - (void)viewWillAppear {
35 | [super viewWillAppear];
36 | [self updateUserInterface];
37 | }
38 |
39 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
40 | [self updateUserInterface];
41 | }
42 |
43 | - (void)controlTextDidChange:(NSNotification *)obj {
44 | NSInteger newValue = self.linesBetweenSettingsTextField.integerValue;
45 | [self setLineSpacing:newValue];
46 | }
47 |
48 | - (IBAction)lineSpacingStepperChanged:(id)sender {
49 | NSInteger newValue = self.linesBetweenSettingsStepper.integerValue;
50 | [self setLineSpacing:newValue];
51 | }
52 | - (IBAction)lineSpacingTextFieldChanged:(id)sender {
53 | NSInteger newValue = self.linesBetweenSettingsTextField.integerValue;
54 | [self setLineSpacing:newValue];
55 | }
56 |
57 | - (void)setLineSpacing:(NSInteger)value {
58 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
59 | BOOL includeBuildSettingsInfo = [defaults boolForKey:TPSIncludeBuildSettingInfoComments];
60 | NSString *lineSpacingKey = includeBuildSettingsInfo ? TPSLinesBetweenBuildSettingsWithInfo : TPSLinesBetweenBuildSettings;
61 | [defaults setInteger:value forKey:lineSpacingKey];
62 | }
63 |
64 | - (void)updateUserInterface {
65 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
66 | BOOL includeBuildSettingsInfo = [defaults boolForKey:TPSIncludeBuildSettingInfoComments];
67 | NSString *lineSpacingKey = includeBuildSettingsInfo ? TPSLinesBetweenBuildSettingsWithInfo : TPSLinesBetweenBuildSettings;
68 | NSInteger linesBetweenSettings = [defaults integerForKey:lineSpacingKey];
69 | [self updateLineSpacingControlsWithLineSpacing:linesBetweenSettings];
70 | [self updateSampleOutputWithLineSpacing:linesBetweenSettings];
71 | }
72 |
73 | - (void)updateLineSpacingControlsWithLineSpacing:(NSInteger)linesBetweenSettings {
74 | self.linesBetweenSettingsTextField.integerValue = linesBetweenSettings;
75 | self.linesBetweenSettingsStepper.integerValue = linesBetweenSettings;
76 | self.linesBetweenSettingsLabel.stringValue = linesBetweenSettings == 1 ? @"line between settings" : @"lines between settings";
77 | }
78 |
79 | - (void)updateSampleOutputWithLineSpacing:(NSInteger)linesBetweenSettings {
80 | NSDictionary *sampleSettings = @{@"CLANG_WARN__DUPLICATE_METHOD_MATCH":@"YES",
81 | @"COPY_PHASE_STRIP":@"YES",
82 | @"DEBUG_INFORMATION_FORMAT":@"dwarf-with-dsym",
83 | @"ENABLE_NS_ASSERTIONS":@"NO"};
84 |
85 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
86 | BOOL includeBuildSettingsInfo = [defaults boolForKey:TPSIncludeBuildSettingInfoComments];
87 | BOOL alignBuildSettings = [defaults boolForKey:TPSAlignBuildSettingValues];
88 |
89 | NSString *testString = [BuildSettingExtractor exampleBuildFormattingStringForSettings:sampleSettings includeBuildSettingInfoComments:includeBuildSettingsInfo alignBuildSettingValues:alignBuildSettings linesBetweenSettings:linesBetweenSettings];
90 | self.exampleOutputTextView.string = testString;
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/FileLayoutPreferencesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // FileLayoutPreferencesViewController.h
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/14/20.
6 | // Copyright © 2020 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface FileLayoutPreferencesViewController : NSViewController
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/FileLayoutPreferencesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // FileLayoutPreferencesViewController.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 2/14/20.
6 | // Copyright © 2020 Tapas Software. All rights reserved.
7 | //
8 |
9 | #import "FileLayoutPreferencesViewController.h"
10 | #import "AppConstants+Categories.h"
11 | #import "SampleFileStructureGenerator.h"
12 | #import "BuildSettingExtractor.h"
13 |
14 | typedef NS_ENUM(NSUInteger, PreferencesInterfaceElementTags) {
15 | ProjectNameTextField = 101,
16 | SharedNameTextField = 102,
17 | SeparatorTextField = 103,
18 | DestinationFolderNameTextField = 104
19 | };
20 |
21 | #pragma mark -
22 |
23 | @interface FileLayoutPreferencesViewController ()
24 | @property (weak) IBOutlet NSOutlineView *outlineView;
25 | @property Item *rootItem;
26 | @end
27 |
28 | @implementation FileLayoutPreferencesViewController
29 |
30 | - (NSSize)preferredContentSize {
31 | return NSMakeSize(744.0, 352.0);
32 | }
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 | NSArray *defaultKeys = @[TPSOutputFileNameShared, TPSOutputFileNameProject, TPSOutputFileNameSeparator, TPSTargetFoldersEnabled, TPSProjectFolderEnabled, TPSDestinationFolderName];
37 | for (NSString *key in defaultKeys) {
38 | [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:key options:0 context:nil];
39 | }
40 | }
41 |
42 | - (void)viewWillAppear {
43 | [super viewWillAppear];
44 | [self updateOutlineView];
45 | }
46 |
47 | - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
48 | if ([fieldEditor.string isEqualToString:@""]) {
49 | if (control.tag == ProjectNameTextField) {
50 | fieldEditor.string = BuildSettingExtractor.defaultProjectConfigName;
51 | }
52 | else if (control.tag == SharedNameTextField) {
53 | fieldEditor.string = BuildSettingExtractor.defaultSharedConfigName;
54 | }
55 | else if (control.tag == DestinationFolderNameTextField) {
56 | fieldEditor.string = BuildSettingExtractor.defaultDestinationFolderName;
57 | }
58 | }
59 | return YES;
60 | }
61 |
62 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
63 | [self updateOutlineView];
64 | }
65 |
66 | - (void)updateOutlineView {
67 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
68 | NSString *sharedName = [defaults stringForKey:TPSOutputFileNameShared];
69 | NSString *projectName = [defaults stringForKey:TPSOutputFileNameProject];
70 | NSString *separator = [defaults stringForKey:TPSOutputFileNameSeparator];
71 | BOOL targetFoldersEnabled = [defaults boolForKey:TPSTargetFoldersEnabled];
72 | BOOL projectFolderEnabled = [defaults boolForKey:TPSProjectFolderEnabled];
73 | NSString *destinationFolderName = [defaults stringForKey:TPSDestinationFolderName];
74 |
75 | self.rootItem = [[[SampleFileStructureGenerator alloc] init] exampleDataForProjectName:projectName sharedName:sharedName wordSeparator:separator useSubfolders:targetFoldersEnabled useProjectFolder:projectFolderEnabled destinationFolderName:destinationFolderName];
76 | [self.outlineView reloadData];
77 | [self.outlineView expandItem:nil expandChildren:YES];
78 | }
79 |
80 | - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(nullable id)item {
81 | if (!item) { return self.rootItem.children.count; }
82 | else { return ((Item *)item).children.count; }
83 | }
84 |
85 | - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(nullable id)item{
86 | if (!item) { return self.rootItem.children[index]; }
87 | else { return ((Item *)item).children[index]; }
88 | }
89 |
90 | - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
91 | if (!item) { return self.rootItem.isFolder; }
92 | else { return ((Item *)item).isFolder; }
93 | }
94 |
95 | - (nullable NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(nullable NSTableColumn *)tableColumn item:(id)item {
96 | Item *outlineItem = item;
97 | NSTableCellView *cell = [outlineView makeViewWithIdentifier:@"OutlineCell" owner:nil];
98 | cell.textField.stringValue = outlineItem.name;
99 | cell.imageView.image = outlineItem.icon;
100 | return cell;
101 | }
102 |
103 | - (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item {
104 | return NO;
105 | }
106 |
107 | - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item {
108 | return NO;
109 | }
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/PreferencesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // PreferencesViewController.h
3 | // PreferencesPrototype
4 | //
5 | // Created by James Dempsey on 1/31/20.
6 | // Copyright © 2020 James Dempsey. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface PreferencesViewController : NSTabViewController
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/PreferencesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // PreferencesViewController.m
3 | // PreferencesPrototype
4 | //
5 | // Created by James Dempsey on 1/31/20.
6 | // Copyright © 2020 James Dempsey. All rights reserved.
7 | //
8 |
9 | #import "PreferencesViewController.h"
10 |
11 | @interface PreferencesViewController ()
12 | @end
13 |
14 | @implementation PreferencesViewController
15 |
16 | - (void)viewDidLoad {
17 | [super viewDidLoad];
18 | // Use SF Symbols for preference pane icons on macOS 11 and later
19 | if (@available(macOS 11.0, *)) {
20 | for (NSTabViewItem *item in self.tabViewItems) {
21 | if ([item.identifier isEqualToString:@"FileLayout"]) {
22 | item.image = [NSImage imageWithSystemSymbolName:@"doc.on.doc" accessibilityDescription:item.label];
23 | }
24 | else if ([item.identifier isEqualToString:@"Contents"]) {
25 | item.image = [NSImage imageWithSystemSymbolName:@"hammer" accessibilityDescription:item.label];
26 | } else {
27 | [NSException raise: NSInternalInconsistencyException format:@"Unexpected preference pane tab item"];
28 | }
29 | }
30 | }
31 | }
32 |
33 | - (void)viewWillAppear {
34 | [super viewWillAppear];
35 | [self updateWindowTitle];
36 | }
37 |
38 | - (void)viewDidAppear {
39 | [super viewDidAppear];
40 | [self.view.window makeFirstResponder:nil];
41 | }
42 |
43 | - (void)updateWindowTitle {
44 | NSTabViewItem *selectedItem = self.tabViewItems[self.selectedTabViewItemIndex];
45 | self.view.window.title = selectedItem.label;
46 | }
47 |
48 | - (NSRect)windowFrameForViewController:(NSViewController *)viewController {
49 | NSRect contentRect = { NSZeroPoint, viewController.preferredContentSize };
50 | NSSize newSize = [self.view.window frameRectForContentRect:contentRect].size;
51 | NSRect windowFrame = self.view.window.frame;
52 | windowFrame.origin.y += windowFrame.size.height - newSize.height;
53 | windowFrame.size = newSize;
54 | return windowFrame;
55 | }
56 |
57 | - (void)transitionFromViewController:(NSViewController *)fromViewController toViewController:(NSViewController *)toViewController options:(NSViewControllerTransitionOptions)options completionHandler:(void (^)(void))completion {
58 | [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
59 | [self updateWindowTitle];
60 | NSRect windowFrame = [self windowFrameForViewController:toViewController];
61 | [self.view.window.animator setFrame:windowFrame display:false];
62 | NSViewControllerTransitionOptions options = NSViewControllerTransitionAllowUserInteraction | NSViewControllerTransitionCrossfade;
63 | [super transitionFromViewController:fromViewController toViewController:toViewController options: options completionHandler:completion];
64 | }];
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/SampleFileStructureGenerator.h:
--------------------------------------------------------------------------------
1 | //
2 | // SampleFileStructureGenerator.h
3 | // SampleOutlineView
4 | //
5 | // Created by James Dempsey on 2/5/20.
6 | // Copyright © 2020 James Dempsey. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface Item: NSObject
15 | @property(copy) NSString *name;
16 | @property NSMutableArray *children;
17 | @property (readonly) BOOL isFolder;
18 | @property (readonly) NSImage *icon;
19 | - (instancetype)initWithName:(NSString *)name;
20 | @end
21 |
22 | @interface SampleFileStructureGenerator : NSObject
23 | - (Item *)exampleDataForProjectName:(NSString *)projectName sharedName:(NSString *)sharedName wordSeparator:(NSString *)wordSeparator useSubfolders:(BOOL)useSubfolders useProjectFolder:(BOOL)useProjectFolder destinationFolderName:(NSString *)destinationFolderName;
24 | @end
25 |
26 | NS_ASSUME_NONNULL_END
27 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/Preferences/SampleFileStructureGenerator.m:
--------------------------------------------------------------------------------
1 | //
2 | // SampleFileStructureGenerator.m
3 | // SampleOutlineView
4 | //
5 | // Created by James Dempsey on 2/5/20.
6 | // Copyright © 2020 James Dempsey. All rights reserved.
7 | //
8 |
9 | #import "SampleFileStructureGenerator.h"
10 |
11 | @implementation Item
12 | - (instancetype)initWithName:(NSString *)name {
13 | self = [super init];
14 | if (self) {
15 | self.name = name;
16 | self.children = [[NSMutableArray alloc] init];
17 | }
18 | return self;
19 | }
20 | - (BOOL)isFolder {
21 | return self.children.count > 0;
22 | }
23 | - (NSImage *)icon {
24 | NSString *iconName = self.isFolder ? @"folder" : @"config_file";
25 | return [NSImage imageNamed:iconName];
26 | }
27 | @end
28 |
29 | @interface NSString (TPS_SpaceReplacement)
30 | -(NSString *)tps_stringReplacingSpacesWithString:(NSString *)separator;
31 | @end
32 | @implementation NSString (TPS_SpaceReplacement)
33 | -(NSString *)tps_stringReplacingSpacesWithString:(NSString *)separator {
34 | return [self stringByReplacingOccurrencesOfString:@" " withString:separator];
35 | }
36 | @end
37 |
38 | @implementation SampleFileStructureGenerator
39 | - (Item *)exampleDataForProjectName:(NSString *)projectName sharedName:(NSString *)sharedName wordSeparator:(NSString *)wordSeparator useSubfolders:(BOOL)useSubfolders useProjectFolder:(BOOL)useProjectFolder destinationFolderName:(NSString *)destinationFolderName {
40 |
41 | NSArray *sortedConfigs = [@[[sharedName tps_stringReplacingSpacesWithString:wordSeparator], @"Debug", @"Release"] sortedArrayUsingSelector:@selector(localizedCompare:)];
42 | NSArray *sortedTargets = [@[[projectName tps_stringReplacingSpacesWithString:wordSeparator], @"MyApp", @"MyAppTests"] sortedArrayUsingSelector:@selector(localizedCompare:)];
43 |
44 | Item *rootItem = [[Item alloc] initWithName:@"Root"];
45 |
46 | Item *destinationItem = [[Item alloc] initWithName:destinationFolderName];
47 | [rootItem.children addObject:destinationItem];
48 |
49 | for (NSString *targetName in sortedTargets) {
50 | BOOL skipProjectFolder = [targetName isEqualToString:projectName] && !useProjectFolder;
51 | if (useSubfolders && !skipProjectFolder) {
52 | Item *targetFolderItem = [[Item alloc] initWithName:targetName];
53 | [self appendChildrenToItem:targetFolderItem withRootName:targetName configNames:sortedConfigs wordSeparator:wordSeparator];
54 | [destinationItem.children addObject:targetFolderItem];
55 |
56 | } else {
57 | [self appendChildrenToItem:destinationItem withRootName:targetName configNames:sortedConfigs wordSeparator:wordSeparator];
58 | }
59 | }
60 |
61 | return rootItem;
62 | }
63 |
64 | - (NSString *)nameForRootName:(NSString *)rootName separator:(NSString *)separator configName:(NSString *)configName {
65 | return [NSString stringWithFormat:@"%@%@%@.xcconfig", rootName, separator, configName];
66 | }
67 |
68 | - (void)appendChildrenToItem:(Item *)item withRootName:(NSString *)rootName configNames:(NSArray *)configNames wordSeparator:(NSString *)wordSeparator {
69 | for (NSString * configName in configNames) {
70 | NSString *fileName = [self nameForRootName:rootName separator:wordSeparator configName:configName];
71 | Item *newItem = [[Item alloc] initWithName:fileName];
72 | [item.children addObject:newItem];
73 | }
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/BuildSettingExtractor/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 9/9/14.
6 | // Copyright (c) 2014 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import Cocoa;
10 |
11 | int main(int argc, const char * argv[])
12 | {
13 | return NSApplicationMain(argc, argv);
14 | }
15 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/BuildSettingExtractor.xctestplan:
--------------------------------------------------------------------------------
1 | {
2 | "configurations" : [
3 | {
4 | "id" : "A59CFD73-15EC-4B32-9336-A775AA56A72E",
5 | "name" : "Standard",
6 | "options" : {
7 |
8 | }
9 | },
10 | {
11 | "id" : "4BF654CB-B6F5-489E-B5A8-99F06E583C46",
12 | "name" : "Thread Sanitizer",
13 | "options" : {
14 | "threadSanitizerEnabled" : true,
15 | "undefinedBehaviorSanitizerEnabled" : true
16 | }
17 | },
18 | {
19 | "id" : "373DC580-59EC-476B-A670-9ADF153957DF",
20 | "name" : "Address Sanitizer",
21 | "options" : {
22 | "addressSanitizer" : {
23 | "enabled" : true
24 | },
25 | "undefinedBehaviorSanitizerEnabled" : true
26 | }
27 | }
28 | ],
29 | "defaultOptions" : {
30 | "codeCoverage" : false,
31 | "targetForVariableExpansion" : {
32 | "containerPath" : "container:BuildSettingExtractor.xcodeproj",
33 | "identifier" : "53719E8E19BF4C6D005D3DE0",
34 | "name" : "BuildSettingExtractor"
35 | }
36 | },
37 | "testTargets" : [
38 | {
39 | "target" : {
40 | "containerPath" : "container:BuildSettingExtractor.xcodeproj",
41 | "identifier" : "53719EAF19BF4C6D005D3DE0",
42 | "name" : "BuildSettingExtractorTests"
43 | }
44 | }
45 | ],
46 | "version" : 1
47 | }
48 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/BuildSettingExtractorTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/BuildSettingExtractorTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // BuildSettingExtractorTests.m
3 | // BuildSettingExtractorTests
4 | //
5 | // Created by James Dempsey on 9/9/14.
6 | // Copyright (c) 2014 Tapas Software. All rights reserved.
7 | //
8 |
9 | @import XCTest;
10 | #import "BuildSettingExtractor.h"
11 | #import "BuildSettingCommentGenerator.h"
12 | #import "BuildSettingInfoSource.h"
13 | #import "Constants+Categories.h"
14 |
15 | @interface NSObject (BuildSettingExtractorMethods)
16 | - (NSDictionary *)buildSettingsByConfigurationForConfigurations:(NSArray *)buildConfigurations;
17 | @end
18 |
19 | @interface BuildSettingExtractorTests : XCTestCase
20 | @end
21 |
22 | @implementation BuildSettingExtractorTests
23 |
24 | - (void)testThreeBuildConfigurations
25 | {
26 | NSURL *testFileURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"ThreeBuildConfigs" withExtension:@"plist"];
27 | NSDictionary *testPlist = [NSDictionary dictionaryWithContentsOfURL:testFileURL];
28 |
29 | NSArray *buildConfigurations = testPlist[@"buildConfigurations"];
30 | NSDictionary *expectedBuildSettings = testPlist[@"expectedBuildSettings"];
31 |
32 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
33 |
34 | NSDictionary *buildSettings = [extractor buildSettingsByConfigurationForConfigurations:buildConfigurations];
35 |
36 | NSDictionary *sharedBuildSettings = buildSettings[extractor.sharedConfigName];
37 |
38 | XCTAssert([sharedBuildSettings isEqualToDictionary:expectedBuildSettings], @"Build settings should match");
39 | }
40 |
41 | - (void)testDictionaryBuildSettingsCategory
42 | {
43 | NSDictionary *dictionaryWithBuildSettings = @{ @"Shared": @"COPY_PHASE_STRIP = NO", @"Release": @"COPY_PHASE_STRIP = NO", @"Debug": @"COPY_PHASE_STRIP = NO" };
44 | XCTAssertTrue(dictionaryWithBuildSettings.tps_containsBuildSettings);
45 |
46 | NSDictionary *dictionaryWithMinimalBuildSettings = @{ @"Shared": @"", @"Release": @"", @"Debug": @"COPY_PHASE_STRIP = NO" };
47 | XCTAssertTrue(dictionaryWithMinimalBuildSettings.tps_containsBuildSettings);
48 |
49 | NSDictionary *dictionaryWithoutBuildSettings = @{ @"Shared": @"", @"Release": @"", @"Debug": @"" };
50 | XCTAssertFalse(dictionaryWithoutBuildSettings.tps_containsBuildSettings);
51 |
52 | NSDictionary *badDictionary = @{@"Shared":@"", @"Release":@"", @"Debug":[NSDate date] };
53 | BOOL result = NO;
54 | XCTAssertThrows(result = badDictionary.tps_containsBuildSettings);
55 | }
56 |
57 | - (void)testLoadingBuildSettingInfo
58 | {
59 | NSError *error = nil;
60 |
61 | // This test assumes Xcode is installed at /Applications/Xcode.app or /Applications/Xcode-beta.app
62 | BuildSettingInfoSource *infoSource = [BuildSettingInfoSource resolvedBuildSettingInfoSourceWithStyle:BuildSettingInfoSourceStyleStandard customURL:nil error:&error];
63 | XCTAssertNotNil(infoSource);
64 | XCTAssertNil(error);
65 |
66 | NSString *activityName = [NSString stringWithFormat:@"Load build setting info from info source '%@' version '%ld'", infoSource.resolvedURL, (long)infoSource.resolvedVersion];
67 | [XCTContext runActivityNamed:activityName block:^(id _Nonnull activity) {
68 | NSError *activityError = nil;
69 | BuildSettingCommentGenerator *commentGenerator = [[BuildSettingCommentGenerator alloc] initWithBuildSettingInfoSource:infoSource];
70 | BOOL success = [commentGenerator loadBuildSettingInfo:&activityError];
71 | XCTAssertTrue(success);
72 | XCTAssertNil(activityError);
73 | }];
74 | }
75 |
76 | - (void)testBadProjectURL
77 | {
78 | NSError *fatalError = nil;
79 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
80 |
81 | NSURL *badURL = [NSURL fileURLWithPath:[@"~/Documents/BadProjectURL.xcodeproj" stringByExpandingTildeInPath]];
82 |
83 | NSArray *nonFatalErrors = [extractor extractBuildSettingsFromProject:badURL error:&fatalError];
84 |
85 | XCTAssertNil(nonFatalErrors);
86 | XCTAssertNotNil(fatalError);
87 | XCTAssertEqual(fatalError.code, 260);
88 |
89 | }
90 |
91 | // Reads the project.pbxproj file inside of BadProject.xcodeproj.test.
92 | // A stripped down xcodeproj bundle with a malformed project.pbxproj plist.
93 | - (void)testMalformedProjectFile
94 | {
95 | NSError *fatalError = nil;
96 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
97 |
98 | NSURL *badProjectURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"BadProject.xcodeproj" withExtension:@"test"];
99 |
100 | NSArray *nonFatalErrors = [extractor extractBuildSettingsFromProject:badProjectURL error:&fatalError];
101 |
102 | XCTAssertNil(nonFatalErrors);
103 | XCTAssertNotNil(fatalError);
104 | XCTAssertEqual(fatalError.code, 3840); // "Junk after plist at line 545"
105 |
106 | }
107 |
108 | // Reads the project.pbxproj file inside of BadVersionNumber.xcodeproj.test.
109 | // A stripped down xcodeproj bundle with its project version set to "Xcode 9999.9"
110 | - (void)testUnsupportedProjectVersion
111 | {
112 | NSError *fatalError = nil;
113 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
114 |
115 | NSURL *badProjectURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"BadVersionNumber.xcodeproj" withExtension:@"test"];
116 |
117 | NSArray *nonFatalErrors = [extractor extractBuildSettingsFromProject:badProjectURL error:&fatalError];
118 |
119 | XCTAssertNil(nonFatalErrors);
120 | XCTAssertNotNil(fatalError);
121 | XCTAssertEqual(fatalError.domain, TPSBuildSettingExtractorErrorDomain);
122 | XCTAssertEqual(fatalError.code, UnsupportedXcodeVersion);
123 | // "Unable to extract build settings from project ‘BadVersionNumber.xcodeproj"
124 | // "Project file format version ‘Xcode 9999.9’ is not supported."
125 |
126 | }
127 |
128 | // Reads the project.pbxproj file inside of ConflictingName.xcodeproj.test.
129 | // A stripped down xcodeproj bundle with a conflicting target name "MyTarget".
130 | - (void)testConflictingProjectName
131 | {
132 | NSError *fatalError = nil;
133 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
134 | extractor.projectConfigName = @"MyTarget";
135 |
136 | NSURL *badProjectURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"ConflictingName.xcodeproj" withExtension:@"test"];
137 |
138 | NSArray *nonFatalErrors = [extractor extractBuildSettingsFromProject:badProjectURL error:&fatalError];
139 |
140 | XCTAssertNotNil(nonFatalErrors);
141 | XCTAssertEqual(nonFatalErrors.count, 1);
142 |
143 | NSError *firstError = nonFatalErrors.firstObject;
144 | XCTAssertNotNil(firstError);
145 | XCTAssertEqual(firstError.domain, TPSBuildSettingExtractorErrorDomain);
146 | XCTAssertEqual(firstError.code, ProjectSettingsNamingConflict);
147 | // "Project settings filename conflict."
148 | // "The target 'MyTarget' has the same name as the project name set in Preferences."
149 |
150 | XCTAssertNil(fatalError);
151 |
152 | }
153 |
154 | // Reads the project.pbxproj file inside of EmptySettings.xcodeproj.test.
155 | // A stripped down xcodeproj bundle with no build settings.
156 | - (void)testEmptySettings
157 | {
158 | NSError *fatalError = nil;
159 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
160 |
161 | NSURL *emptySettingsProjectURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"EmptySettings.xcodeproj" withExtension:@"test"];
162 |
163 | NSArray *nonFatalErrors = [extractor extractBuildSettingsFromProject:emptySettingsProjectURL error:&fatalError];
164 |
165 | XCTAssertNil(nonFatalErrors);
166 | XCTAssertNotNil(fatalError);
167 | XCTAssertEqual(fatalError.domain, TPSBuildSettingExtractorErrorDomain);
168 | XCTAssertEqual(fatalError.code, NoSettingsFoundInProjectFile);
169 | // "No settings were found in the project 'EmptySettings.xcodeproj.test'."
170 | // "The project may already be using .xcconfig files for its build settings."
171 | // "No xcconfig files will be written."
172 |
173 | }
174 |
175 | - (void)testWritingWithoutExtracting
176 | {
177 | NSError *fatalError = nil;
178 | BuildSettingExtractor *extractor = [[BuildSettingExtractor alloc] init];
179 |
180 | NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
181 |
182 | XCTAssertThrows([extractor writeConfigFilesToDestinationFolder:tempFolder error:&fatalError]);
183 | }
184 |
185 | - (void)testUnresolvedBuildSettingInfoSource {
186 |
187 | // NOTE: -1 is special undeclared style for testing failure to resolve an info source.
188 | // It causes BuildSettingInfoSource to use invalid paths for Xcode and Xcode-beta.
189 | BuildSettingInfoSourceStyle failureStyle = -1;
190 |
191 | NSError *error = nil;
192 | BuildSettingInfoSource *source = [BuildSettingInfoSource resolvedBuildSettingInfoSourceWithStyle:failureStyle customURL:nil error:&error];
193 | XCTAssertNil(source);
194 | XCTAssertNotNil(error);
195 | XCTAssertEqual(error.domain, TPSBuildSettingExtractorErrorDomain);
196 | XCTAssertEqual(error.code, BuildSettingInfoSourceNotFound);
197 | }
198 |
199 | - (void)testBuildSettingFormatting {
200 | NSURL *testFileURL = [[NSBundle bundleForClass:[BuildSettingExtractorTests class]] URLForResource:@"FormattingTestData" withExtension:@"plist"];
201 | NSDictionary *testPlist = [NSDictionary dictionaryWithContentsOfURL:testFileURL];
202 |
203 | NSArray *formattingTests = testPlist[@"formattingTests"];
204 |
205 | NSDictionary *testSettings = @{@"CLANG_WARN__DUPLICATE_METHOD_MATCH":@"YES",
206 | @"COPY_PHASE_STRIP":@"YES",
207 | @"DEBUG_INFORMATION_FORMAT":@"dwarf-with-dsym",
208 | @"ENABLE_NS_ASSERTIONS":@"NO"};
209 |
210 | for (NSDictionary *test in formattingTests) {
211 | NSNumber *includeBuildSettingInfoValue = [test valueForKey:@"includeBuildSettingInfo"];
212 | XCTAssertNotNil(includeBuildSettingInfoValue);
213 | BOOL includeBuildSettingsInfo = [includeBuildSettingInfoValue boolValue];
214 |
215 | NSNumber *alignBuildSettingsValue = [test valueForKey:@"alignSettings"];
216 | XCTAssertNotNil(alignBuildSettingsValue);
217 | BOOL alignBuildSettings = [alignBuildSettingsValue boolValue];
218 |
219 | NSNumber *linesBetweenSettingsValue = [test valueForKey:@"linesBetweenSettings"];
220 | XCTAssertNotNil(linesBetweenSettingsValue);
221 | NSInteger linesBetweenSettings = [linesBetweenSettingsValue integerValue];
222 |
223 | NSString *expectedResult = [test valueForKey:@"expectedResult"];
224 | XCTAssertNotNil(expectedResult);
225 |
226 | NSString *testString = [BuildSettingExtractor exampleBuildFormattingStringForSettings:testSettings includeBuildSettingInfoComments:includeBuildSettingsInfo alignBuildSettingValues:alignBuildSettings linesBetweenSettings:linesBetweenSettings];
227 |
228 | XCTAssertEqualObjects(testString, expectedResult);
229 |
230 | }
231 |
232 | }
233 |
234 | @end
235 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/TestFiles/ConflictingName.xcodeproj.test/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 53FA8808234103D500F51EC3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA8807234103D500F51EC3 /* AppDelegate.m */; };
11 | 53FA880B234103D500F51EC3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA880A234103D500F51EC3 /* ViewController.m */; };
12 | 53FA880D234103D700F51EC3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53FA880C234103D700F51EC3 /* Assets.xcassets */; };
13 | 53FA8810234103D700F51EC3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53FA880E234103D700F51EC3 /* Main.storyboard */; };
14 | 53FA8813234103D700F51EC3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA8812234103D700F51EC3 /* main.m */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | 53FA8803234103D500F51EC3 /* MyTarget.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTarget.app; sourceTree = BUILT_PRODUCTS_DIR; };
19 | 53FA8806234103D500F51EC3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
20 | 53FA8807234103D500F51EC3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
21 | 53FA8809234103D500F51EC3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
22 | 53FA880A234103D500F51EC3 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
23 | 53FA880C234103D700F51EC3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | 53FA880F234103D700F51EC3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 53FA8811234103D700F51EC3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | 53FA8812234103D700F51EC3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
27 | 53FA8814234103D700F51EC3 /* MyTarget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MyTarget.entitlements; sourceTree = ""; };
28 | /* End PBXFileReference section */
29 |
30 | /* Begin PBXFrameworksBuildPhase section */
31 | 53FA8800234103D500F51EC3 /* Frameworks */ = {
32 | isa = PBXFrameworksBuildPhase;
33 | buildActionMask = 2147483647;
34 | files = (
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | 53FA87FA234103D500F51EC3 = {
42 | isa = PBXGroup;
43 | children = (
44 | 53FA8805234103D500F51EC3 /* MyTarget */,
45 | 53FA8804234103D500F51EC3 /* Products */,
46 | );
47 | sourceTree = "";
48 | };
49 | 53FA8804234103D500F51EC3 /* Products */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 53FA8803234103D500F51EC3 /* MyTarget.app */,
53 | );
54 | name = Products;
55 | sourceTree = "";
56 | };
57 | 53FA8805234103D500F51EC3 /* MyTarget */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 53FA8806234103D500F51EC3 /* AppDelegate.h */,
61 | 53FA8807234103D500F51EC3 /* AppDelegate.m */,
62 | 53FA8809234103D500F51EC3 /* ViewController.h */,
63 | 53FA880A234103D500F51EC3 /* ViewController.m */,
64 | 53FA880C234103D700F51EC3 /* Assets.xcassets */,
65 | 53FA880E234103D700F51EC3 /* Main.storyboard */,
66 | 53FA8811234103D700F51EC3 /* Info.plist */,
67 | 53FA8812234103D700F51EC3 /* main.m */,
68 | 53FA8814234103D700F51EC3 /* MyTarget.entitlements */,
69 | );
70 | path = MyTarget;
71 | sourceTree = "";
72 | };
73 | /* End PBXGroup section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | 53FA8802234103D500F51EC3 /* MyTarget */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = 53FA8817234103D700F51EC3 /* Build configuration list for PBXNativeTarget "MyTarget" */;
79 | buildPhases = (
80 | 53FA87FF234103D500F51EC3 /* Sources */,
81 | 53FA8800234103D500F51EC3 /* Frameworks */,
82 | 53FA8801234103D500F51EC3 /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = MyTarget;
89 | productName = MyTarget;
90 | productReference = 53FA8803234103D500F51EC3 /* MyTarget.app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | 53FA87FB234103D500F51EC3 /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastUpgradeCheck = 1100;
100 | ORGANIZATIONNAME = Test;
101 | TargetAttributes = {
102 | 53FA8802234103D500F51EC3 = {
103 | CreatedOnToolsVersion = 11.0;
104 | };
105 | };
106 | };
107 | buildConfigurationList = 53FA87FE234103D500F51EC3 /* Build configuration list for PBXProject "MyTarget" */;
108 | compatibilityVersion = "Xcode 9.3";
109 | developmentRegion = en;
110 | hasScannedForEncodings = 0;
111 | knownRegions = (
112 | en,
113 | Base,
114 | );
115 | mainGroup = 53FA87FA234103D500F51EC3;
116 | productRefGroup = 53FA8804234103D500F51EC3 /* Products */;
117 | projectDirPath = "";
118 | projectRoot = "";
119 | targets = (
120 | 53FA8802234103D500F51EC3 /* MyTarget */,
121 | );
122 | };
123 | /* End PBXProject section */
124 |
125 | /* Begin PBXResourcesBuildPhase section */
126 | 53FA8801234103D500F51EC3 /* Resources */ = {
127 | isa = PBXResourcesBuildPhase;
128 | buildActionMask = 2147483647;
129 | files = (
130 | 53FA880D234103D700F51EC3 /* Assets.xcassets in Resources */,
131 | 53FA8810234103D700F51EC3 /* Main.storyboard in Resources */,
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | /* End PBXResourcesBuildPhase section */
136 |
137 | /* Begin PBXSourcesBuildPhase section */
138 | 53FA87FF234103D500F51EC3 /* Sources */ = {
139 | isa = PBXSourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | 53FA880B234103D500F51EC3 /* ViewController.m in Sources */,
143 | 53FA8813234103D700F51EC3 /* main.m in Sources */,
144 | 53FA8808234103D500F51EC3 /* AppDelegate.m in Sources */,
145 | );
146 | runOnlyForDeploymentPostprocessing = 0;
147 | };
148 | /* End PBXSourcesBuildPhase section */
149 |
150 | /* Begin PBXVariantGroup section */
151 | 53FA880E234103D700F51EC3 /* Main.storyboard */ = {
152 | isa = PBXVariantGroup;
153 | children = (
154 | 53FA880F234103D700F51EC3 /* Base */,
155 | );
156 | name = Main.storyboard;
157 | sourceTree = "";
158 | };
159 | /* End PBXVariantGroup section */
160 |
161 | /* Begin XCBuildConfiguration section */
162 | 53FA8815234103D700F51EC3 /* Debug */ = {
163 | isa = XCBuildConfiguration;
164 | buildSettings = {
165 | ALWAYS_SEARCH_USER_PATHS = NO;
166 | CLANG_ANALYZER_NONNULL = YES;
167 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
168 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
169 | CLANG_CXX_LIBRARY = "libc++";
170 | CLANG_ENABLE_MODULES = YES;
171 | CLANG_ENABLE_OBJC_ARC = YES;
172 | CLANG_ENABLE_OBJC_WEAK = YES;
173 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
174 | CLANG_WARN_BOOL_CONVERSION = YES;
175 | CLANG_WARN_COMMA = YES;
176 | CLANG_WARN_CONSTANT_CONVERSION = YES;
177 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
178 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
179 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
180 | CLANG_WARN_EMPTY_BODY = YES;
181 | CLANG_WARN_ENUM_CONVERSION = YES;
182 | CLANG_WARN_INFINITE_RECURSION = YES;
183 | CLANG_WARN_INT_CONVERSION = YES;
184 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
185 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
186 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
188 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
189 | CLANG_WARN_STRICT_PROTOTYPES = YES;
190 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
191 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
192 | CLANG_WARN_UNREACHABLE_CODE = YES;
193 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
194 | COPY_PHASE_STRIP = NO;
195 | DEBUG_INFORMATION_FORMAT = dwarf;
196 | ENABLE_STRICT_OBJC_MSGSEND = YES;
197 | ENABLE_TESTABILITY = YES;
198 | GCC_C_LANGUAGE_STANDARD = gnu11;
199 | GCC_DYNAMIC_NO_PIC = NO;
200 | GCC_NO_COMMON_BLOCKS = YES;
201 | GCC_OPTIMIZATION_LEVEL = 0;
202 | GCC_PREPROCESSOR_DEFINITIONS = (
203 | "DEBUG=1",
204 | "$(inherited)",
205 | );
206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
208 | GCC_WARN_UNDECLARED_SELECTOR = YES;
209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
210 | GCC_WARN_UNUSED_FUNCTION = YES;
211 | GCC_WARN_UNUSED_VARIABLE = YES;
212 | MACOSX_DEPLOYMENT_TARGET = 10.14;
213 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
214 | MTL_FAST_MATH = YES;
215 | ONLY_ACTIVE_ARCH = YES;
216 | SDKROOT = macosx;
217 | };
218 | name = Debug;
219 | };
220 | 53FA8816234103D700F51EC3 /* Release */ = {
221 | isa = XCBuildConfiguration;
222 | buildSettings = {
223 | ALWAYS_SEARCH_USER_PATHS = NO;
224 | CLANG_ANALYZER_NONNULL = YES;
225 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
227 | CLANG_CXX_LIBRARY = "libc++";
228 | CLANG_ENABLE_MODULES = YES;
229 | CLANG_ENABLE_OBJC_ARC = YES;
230 | CLANG_ENABLE_OBJC_WEAK = YES;
231 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
232 | CLANG_WARN_BOOL_CONVERSION = YES;
233 | CLANG_WARN_COMMA = YES;
234 | CLANG_WARN_CONSTANT_CONVERSION = YES;
235 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
237 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
238 | CLANG_WARN_EMPTY_BODY = YES;
239 | CLANG_WARN_ENUM_CONVERSION = YES;
240 | CLANG_WARN_INFINITE_RECURSION = YES;
241 | CLANG_WARN_INT_CONVERSION = YES;
242 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
243 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
244 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
246 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
247 | CLANG_WARN_STRICT_PROTOTYPES = YES;
248 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
249 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
250 | CLANG_WARN_UNREACHABLE_CODE = YES;
251 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
252 | COPY_PHASE_STRIP = NO;
253 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
254 | ENABLE_NS_ASSERTIONS = NO;
255 | ENABLE_STRICT_OBJC_MSGSEND = YES;
256 | GCC_C_LANGUAGE_STANDARD = gnu11;
257 | GCC_NO_COMMON_BLOCKS = YES;
258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
260 | GCC_WARN_UNDECLARED_SELECTOR = YES;
261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
262 | GCC_WARN_UNUSED_FUNCTION = YES;
263 | GCC_WARN_UNUSED_VARIABLE = YES;
264 | MACOSX_DEPLOYMENT_TARGET = 10.14;
265 | MTL_ENABLE_DEBUG_INFO = NO;
266 | MTL_FAST_MATH = YES;
267 | SDKROOT = macosx;
268 | };
269 | name = Release;
270 | };
271 | 53FA8818234103D700F51EC3 /* Debug */ = {
272 | isa = XCBuildConfiguration;
273 | buildSettings = {
274 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
275 | CODE_SIGN_ENTITLEMENTS = MyTarget/MyTarget.entitlements;
276 | CODE_SIGN_STYLE = Automatic;
277 | COMBINE_HIDPI_IMAGES = YES;
278 | INFOPLIST_FILE = MyTarget/Info.plist;
279 | LD_RUNPATH_SEARCH_PATHS = (
280 | "$(inherited)",
281 | "@executable_path/../Frameworks",
282 | );
283 | PRODUCT_BUNDLE_IDENTIFIER = com.example.test.MyTarget;
284 | PRODUCT_NAME = "$(TARGET_NAME)";
285 | };
286 | name = Debug;
287 | };
288 | 53FA8819234103D700F51EC3 /* Release */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
292 | CODE_SIGN_ENTITLEMENTS = MyTarget/MyTarget.entitlements;
293 | CODE_SIGN_STYLE = Automatic;
294 | COMBINE_HIDPI_IMAGES = YES;
295 | INFOPLIST_FILE = MyTarget/Info.plist;
296 | LD_RUNPATH_SEARCH_PATHS = (
297 | "$(inherited)",
298 | "@executable_path/../Frameworks",
299 | );
300 | PRODUCT_BUNDLE_IDENTIFIER = com.example.test.MyTarget;
301 | PRODUCT_NAME = "$(TARGET_NAME)";
302 | };
303 | name = Release;
304 | };
305 | /* End XCBuildConfiguration section */
306 |
307 | /* Begin XCConfigurationList section */
308 | 53FA87FE234103D500F51EC3 /* Build configuration list for PBXProject "MyTarget" */ = {
309 | isa = XCConfigurationList;
310 | buildConfigurations = (
311 | 53FA8815234103D700F51EC3 /* Debug */,
312 | 53FA8816234103D700F51EC3 /* Release */,
313 | );
314 | defaultConfigurationIsVisible = 0;
315 | defaultConfigurationName = Release;
316 | };
317 | 53FA8817234103D700F51EC3 /* Build configuration list for PBXNativeTarget "MyTarget" */ = {
318 | isa = XCConfigurationList;
319 | buildConfigurations = (
320 | 53FA8818234103D700F51EC3 /* Debug */,
321 | 53FA8819234103D700F51EC3 /* Release */,
322 | );
323 | defaultConfigurationIsVisible = 0;
324 | defaultConfigurationName = Release;
325 | };
326 | /* End XCConfigurationList section */
327 | };
328 | rootObject = 53FA87FB234103D500F51EC3 /* Project object */;
329 | }
330 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/TestFiles/EmptySettings.xcodeproj.test/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 53FA8808234103D500F51EC3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA8807234103D500F51EC3 /* AppDelegate.m */; };
11 | 53FA880B234103D500F51EC3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA880A234103D500F51EC3 /* ViewController.m */; };
12 | 53FA880D234103D700F51EC3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53FA880C234103D700F51EC3 /* Assets.xcassets */; };
13 | 53FA8810234103D700F51EC3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53FA880E234103D700F51EC3 /* Main.storyboard */; };
14 | 53FA8813234103D700F51EC3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA8812234103D700F51EC3 /* main.m */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | 53FA8803234103D500F51EC3 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; };
19 | 53FA8806234103D500F51EC3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
20 | 53FA8807234103D500F51EC3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
21 | 53FA8809234103D500F51EC3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
22 | 53FA880A234103D500F51EC3 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
23 | 53FA880C234103D700F51EC3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | 53FA880F234103D700F51EC3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 53FA8811234103D700F51EC3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | 53FA8812234103D700F51EC3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
27 | 53FA8814234103D700F51EC3 /* MyTarget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MyTarget.entitlements; sourceTree = ""; };
28 | /* End PBXFileReference section */
29 |
30 | /* Begin PBXFrameworksBuildPhase section */
31 | 53FA8800234103D500F51EC3 /* Frameworks */ = {
32 | isa = PBXFrameworksBuildPhase;
33 | buildActionMask = 2147483647;
34 | files = (
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | 53FA87FA234103D500F51EC3 = {
42 | isa = PBXGroup;
43 | children = (
44 | 53FA8805234103D500F51EC3 /* MyTarget */,
45 | 53FA8804234103D500F51EC3 /* Products */,
46 | );
47 | sourceTree = "";
48 | };
49 | 53FA8804234103D500F51EC3 /* Products */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 53FA8803234103D500F51EC3 /* .app */,
53 | );
54 | name = Products;
55 | sourceTree = "";
56 | };
57 | 53FA8805234103D500F51EC3 /* MyTarget */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 53FA8806234103D500F51EC3 /* AppDelegate.h */,
61 | 53FA8807234103D500F51EC3 /* AppDelegate.m */,
62 | 53FA8809234103D500F51EC3 /* ViewController.h */,
63 | 53FA880A234103D500F51EC3 /* ViewController.m */,
64 | 53FA880C234103D700F51EC3 /* Assets.xcassets */,
65 | 53FA880E234103D700F51EC3 /* Main.storyboard */,
66 | 53FA8811234103D700F51EC3 /* Info.plist */,
67 | 53FA8812234103D700F51EC3 /* main.m */,
68 | 53FA8814234103D700F51EC3 /* MyTarget.entitlements */,
69 | );
70 | path = MyTarget;
71 | sourceTree = "";
72 | };
73 | /* End PBXGroup section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | 53FA8802234103D500F51EC3 /* MyTarget */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = 53FA8817234103D700F51EC3 /* Build configuration list for PBXNativeTarget "MyTarget" */;
79 | buildPhases = (
80 | 53FA87FF234103D500F51EC3 /* Sources */,
81 | 53FA8800234103D500F51EC3 /* Frameworks */,
82 | 53FA8801234103D500F51EC3 /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = MyTarget;
89 | productName = MyTarget;
90 | productReference = 53FA8803234103D500F51EC3 /* .app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | 53FA87FB234103D500F51EC3 /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastUpgradeCheck = 1100;
100 | ORGANIZATIONNAME = Test;
101 | TargetAttributes = {
102 | 53FA8802234103D500F51EC3 = {
103 | CreatedOnToolsVersion = 11.0;
104 | };
105 | };
106 | };
107 | buildConfigurationList = 53FA87FE234103D500F51EC3 /* Build configuration list for PBXProject "MyTarget" */;
108 | compatibilityVersion = "Xcode 9.3";
109 | developmentRegion = en;
110 | hasScannedForEncodings = 0;
111 | knownRegions = (
112 | en,
113 | Base,
114 | );
115 | mainGroup = 53FA87FA234103D500F51EC3;
116 | productRefGroup = 53FA8804234103D500F51EC3 /* Products */;
117 | projectDirPath = "";
118 | projectRoot = "";
119 | targets = (
120 | 53FA8802234103D500F51EC3 /* MyTarget */,
121 | );
122 | };
123 | /* End PBXProject section */
124 |
125 | /* Begin PBXResourcesBuildPhase section */
126 | 53FA8801234103D500F51EC3 /* Resources */ = {
127 | isa = PBXResourcesBuildPhase;
128 | buildActionMask = 2147483647;
129 | files = (
130 | 53FA880D234103D700F51EC3 /* Assets.xcassets in Resources */,
131 | 53FA8810234103D700F51EC3 /* Main.storyboard in Resources */,
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | /* End PBXResourcesBuildPhase section */
136 |
137 | /* Begin PBXSourcesBuildPhase section */
138 | 53FA87FF234103D500F51EC3 /* Sources */ = {
139 | isa = PBXSourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | 53FA880B234103D500F51EC3 /* ViewController.m in Sources */,
143 | 53FA8813234103D700F51EC3 /* main.m in Sources */,
144 | 53FA8808234103D500F51EC3 /* AppDelegate.m in Sources */,
145 | );
146 | runOnlyForDeploymentPostprocessing = 0;
147 | };
148 | /* End PBXSourcesBuildPhase section */
149 |
150 | /* Begin PBXVariantGroup section */
151 | 53FA880E234103D700F51EC3 /* Main.storyboard */ = {
152 | isa = PBXVariantGroup;
153 | children = (
154 | 53FA880F234103D700F51EC3 /* Base */,
155 | );
156 | name = Main.storyboard;
157 | sourceTree = "";
158 | };
159 | /* End PBXVariantGroup section */
160 |
161 | /* Begin XCBuildConfiguration section */
162 | 53FA8815234103D700F51EC3 /* Debug */ = {
163 | isa = XCBuildConfiguration;
164 | buildSettings = {
165 | };
166 | name = Debug;
167 | };
168 | 53FA8816234103D700F51EC3 /* Release */ = {
169 | isa = XCBuildConfiguration;
170 | buildSettings = {
171 | };
172 | name = Release;
173 | };
174 | 53FA8818234103D700F51EC3 /* Debug */ = {
175 | isa = XCBuildConfiguration;
176 | buildSettings = {
177 | };
178 | name = Debug;
179 | };
180 | 53FA8819234103D700F51EC3 /* Release */ = {
181 | isa = XCBuildConfiguration;
182 | buildSettings = {
183 | };
184 | name = Release;
185 | };
186 | /* End XCBuildConfiguration section */
187 |
188 | /* Begin XCConfigurationList section */
189 | 53FA87FE234103D500F51EC3 /* Build configuration list for PBXProject "MyTarget" */ = {
190 | isa = XCConfigurationList;
191 | buildConfigurations = (
192 | 53FA8815234103D700F51EC3 /* Debug */,
193 | 53FA8816234103D700F51EC3 /* Release */,
194 | );
195 | defaultConfigurationIsVisible = 0;
196 | defaultConfigurationName = Release;
197 | };
198 | 53FA8817234103D700F51EC3 /* Build configuration list for PBXNativeTarget "MyTarget" */ = {
199 | isa = XCConfigurationList;
200 | buildConfigurations = (
201 | 53FA8818234103D700F51EC3 /* Debug */,
202 | 53FA8819234103D700F51EC3 /* Release */,
203 | );
204 | defaultConfigurationIsVisible = 0;
205 | defaultConfigurationName = Release;
206 | };
207 | /* End XCConfigurationList section */
208 | };
209 | rootObject = 53FA87FB234103D500F51EC3 /* Project object */;
210 | }
211 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/TestFiles/FormattingTestData.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | formattingTests
6 |
7 |
8 | alignSettings
9 |
10 | linesBetweenSettings
11 | 3
12 | includeBuildSettingInfo
13 |
14 | expectedResult
15 | // Duplicate Method Definitions
16 | //
17 | // Warn about declaring the same method more than once within the same `@interface`.
18 |
19 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
20 |
21 |
22 |
23 | // Strip Debug Symbols During Copy
24 | //
25 | // Specifies whether binary files that are copied during the build, such as in a Copy
26 | // Bundle Resources or Copy Files build phase, should be stripped of debugging symbols.
27 | // It does not cause the linked product of a target to be stripped—use
28 | // `STRIP_INSTALLED_PRODUCT` for that.
29 |
30 | COPY_PHASE_STRIP = YES
31 |
32 |
33 |
34 | // Debug Information Format
35 | //
36 | // The type of debug information to produce.
37 | //
38 | // * DWARF: Object files and linked products will use DWARF as the debug information
39 | // format. [dwarf]
40 | // * DWARF with dSYM File: Object files and linked products will use DWARF as the debug
41 | // information format, and Xcode will also produce a dSYM file containing the debug
42 | // information from the individual object files (except that a dSYM file is not needed
43 | // and will not be created for static library or object file products). [dwarf-with-dsym]
44 |
45 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
46 |
47 |
48 |
49 | // Enable Foundation Assertions
50 | //
51 | // Controls whether assertion logic provided by `NSAssert` is included in the
52 | // preprocessed source code or is elided during preprocessing. Disabling assertions can
53 | // improve code performance.
54 |
55 | ENABLE_NS_ASSERTIONS = NO
56 |
57 |
58 |
59 | alignSettings
60 |
61 | linesBetweenSettings
62 | 0
63 | includeBuildSettingInfo
64 |
65 | expectedResult
66 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
67 | COPY_PHASE_STRIP = YES
68 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
69 | ENABLE_NS_ASSERTIONS = NO
70 |
71 |
72 |
73 | alignSettings
74 |
75 | linesBetweenSettings
76 | 0
77 | includeBuildSettingInfo
78 |
79 | expectedResult
80 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
81 | COPY_PHASE_STRIP = YES
82 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
83 | ENABLE_NS_ASSERTIONS = NO
84 |
85 |
86 |
87 | alignSettings
88 |
89 | linesBetweenSettings
90 | 1
91 | includeBuildSettingInfo
92 |
93 | expectedResult
94 | // Duplicate Method Definitions
95 | //
96 | // Warn about declaring the same method more than once within the same `@interface`.
97 |
98 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
99 |
100 | // Strip Debug Symbols During Copy
101 | //
102 | // Specifies whether binary files that are copied during the build, such as in a Copy
103 | // Bundle Resources or Copy Files build phase, should be stripped of debugging symbols.
104 | // It does not cause the linked product of a target to be stripped—use
105 | // `STRIP_INSTALLED_PRODUCT` for that.
106 |
107 | COPY_PHASE_STRIP = YES
108 |
109 | // Debug Information Format
110 | //
111 | // The type of debug information to produce.
112 | //
113 | // * DWARF: Object files and linked products will use DWARF as the debug information
114 | // format. [dwarf]
115 | // * DWARF with dSYM File: Object files and linked products will use DWARF as the debug
116 | // information format, and Xcode will also produce a dSYM file containing the debug
117 | // information from the individual object files (except that a dSYM file is not needed
118 | // and will not be created for static library or object file products). [dwarf-with-dsym]
119 |
120 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
121 |
122 | // Enable Foundation Assertions
123 | //
124 | // Controls whether assertion logic provided by `NSAssert` is included in the
125 | // preprocessed source code or is elided during preprocessing. Disabling assertions can
126 | // improve code performance.
127 |
128 | ENABLE_NS_ASSERTIONS = NO
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/TestFiles/ThreeBuildConfigs.plist:
--------------------------------------------------------------------------------
1 | {
2 | buildConfigurations = (
3 | {
4 | isa = XCBuildConfiguration;
5 | buildSettings = {
6 | ALWAYS_SEARCH_USER_PATHS = NO;
7 | ARCHS = "$(ARCHS_STANDARD_64_BIT)";
8 | CLANG_ENABLE_OBJC_ARC = YES;
9 | COPY_PHASE_STRIP = NO;
10 | GCC_C_LANGUAGE_STANDARD = gnu99;
11 | GCC_DYNAMIC_NO_PIC = NO;
12 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
13 | GCC_OPTIMIZATION_LEVEL = 0;
14 | GCC_PREPROCESSOR_DEFINITIONS = (
15 | "DEBUG=1",
16 | "$(inherited)",
17 | );
18 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
19 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
20 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
21 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
22 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
23 | GCC_WARN_UNUSED_VARIABLE = YES;
24 | MACOSX_DEPLOYMENT_TARGET = 10.7;
25 | ONLY_ACTIVE_ARCH = YES;
26 | SDKROOT = macosx;
27 | };
28 | name = Debug;
29 | },
30 | {
31 | isa = XCBuildConfiguration;
32 | buildSettings = {
33 | ALWAYS_SEARCH_USER_PATHS = NO;
34 | ARCHS = "$(ARCHS_STANDARD_64_BIT)";
35 | CLANG_ENABLE_OBJC_ARC = YES;
36 | COPY_PHASE_STRIP = YES;
37 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
38 | GCC_C_LANGUAGE_STANDARD = gnu99;
39 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
40 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
41 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
42 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
43 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
44 | GCC_WARN_UNUSED_VARIABLE = YES;
45 | MACOSX_DEPLOYMENT_TARGET = 10.7;
46 | SDKROOT = macosx;
47 | };
48 | name = Release;
49 | },
50 | {
51 | isa = XCBuildConfiguration;
52 | buildSettings = {
53 | ALWAYS_SEARCH_USER_PATHS = NO;
54 | SDKROOT = macosx;
55 | MACOSX_DEPLOYMENT_TARGET = 10.7;
56 | GCC_WARN_UNUSED_VARIABLE = NO;
57 | /* ARCHS = "$(ARCHS_STANDARD_64_BIT)";
58 | CLANG_ENABLE_OBJC_ARC = YES;
59 | COPY_PHASE_STRIP = YES;
60 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
61 | GCC_C_LANGUAGE_STANDARD = gnu99;
62 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
63 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
64 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
65 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
66 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
67 | */
68 | };
69 | name = Other;
70 | }
71 | );
72 |
73 | expectedBuildSettings = {
74 | ALWAYS_SEARCH_USER_PATHS = NO;
75 | SDKROOT = macosx;
76 | MACOSX_DEPLOYMENT_TARGET = 10.7;
77 | /* ARCHS = "$(ARCHS_STANDARD_64_BIT)";
78 | CLANG_ENABLE_OBJC_ARC = YES;
79 | COPY_PHASE_STRIP = YES;
80 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
81 | GCC_C_LANGUAGE_STANDARD = gnu99;
82 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
83 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
84 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
85 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
86 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
87 | GCC_WARN_UNUSED_VARIABLE = YES;
88 | MACOSX_DEPLOYMENT_TARGET = 10.7;
89 | SDKROOT = macosx;
90 | */
91 | };
92 | }
--------------------------------------------------------------------------------
/BuildSettingExtractorTests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Config/AppConfig.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // AppConfig.xcconfig
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 11/11/22.
6 | // Copyright © 2022 Tapas Software. All rights reserved.
7 | //
8 |
9 | // Configuration settings file format documentation can be found at:
10 | // https://help.apple.com/xcode/#/dev745c5c974
11 |
12 | // Product Bundle Identifier
13 |
14 | // A string that uniquely identifies the bundle. The string should be in
15 | // reverse DNS format using only alphanumeric characters (A-Z, a-z, 0-9),
16 | // the dot (.), and the hyphen (-). This value is used as the
17 | // CFBundleIdentifier in the Info.plist of the built bundle.
18 |
19 | PRODUCT_BUNDLE_IDENTIFIER = net.tapas-software.${PRODUCT_NAME:rfc1034identifier}
20 |
21 | // Allows private app config info to be set and overridden for local dev builds
22 | #include? "/Users/Shared/git/xcconfig-private/CodeSigning.xcconfig"
23 |
24 | // Allows private app config information to be set and overridden for CI builds
25 | #include? "PrivateAppConfig.xcconfig"
26 |
27 |
--------------------------------------------------------------------------------
/Config/ProjectConfig.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // BSE-CodeSigning.xcconfig
3 | // BuildSettingExtractor
4 | //
5 | // Created by James Dempsey on 10/2/19.
6 | // Copyright © 2019 Tapas Software. All rights reserved.
7 | //
8 |
9 | // Configuration settings file format documentation can be found at:
10 | // https://help.apple.com/xcode/#/dev745c5c974
11 |
12 |
13 | // This file optionally includes an xcconfig file outside of the main
14 | // github repository. This allows for codesigning the app without needing
15 | // to include private values, such as team identifiers, in the public
16 | // repository.
17 |
18 | // The optional #include? directive allows the open source project to compile
19 | // when the private xcconfig file is not found.
20 |
21 | #include? "/usr/local/etc/xcconfig-private/CodeSigning.xcconfig"
22 | #include? "/Users/Shared/git/xcconfig-private/CodeSigning.xcconfig"
23 |
24 |
25 | // Code Sign Style
26 |
27 | // This setting specifies the method used to acquire and locate signing assets.
28 | // Choose `Automatic` to let Xcode automatically create and update profiles,
29 | // app IDs, and certificates. Choose `Manual` to create and update these yourself
30 | // on the developer website.
31 |
32 | // Set to Automatic mode.
33 |
34 | CODE_SIGN_STYLE = Automatic
35 |
36 |
37 | // Code Signing Identity
38 |
39 | // The name, also known as the common name, of a valid code-signing certificate
40 | // in a keychain within your keychain path. A missing or invalid certificate
41 | // will cause a build error.
42 |
43 | CODE_SIGN_IDENTITY = Apple Development
44 |
45 |
46 | // Allows private project config information to be set and overridden for production builds
47 | #include? "PrivateProjectConfig.xcconfig"
48 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | BuildSettingExtractor Read Me
2 | =================================
3 |
4 | This is a utility to extract build configuration settings from an Xcode project into a set of xcconfig files.
5 |
6 | The current release, pre-built, code-signed, and notarized is available for download at [https://buildsettingextractor.com](https://buildsettingextractor.com).
7 |
8 | If you decide to move Xcode build settings out of your project file and into xcconfig files, this utility can make that initial move easier. It’s also an easy way for the curious to take a look at the build settings in a project without fear of accidentally changing them.
9 |
10 | For each target and the project itself, BuildSettingExtractor will generate one xcconfig file per build configuration plus a shared xcconfig file with all shared build settings for that target.
11 |
12 | Using the app:
13 |
14 | 1. Launch BuildSettingExtractor
15 | 2. Drag an Xcode Project file (xcodeproj) to the app window
16 | 3. Choose a destination folder
17 |
18 | Choose Preferences… (Command-,) from the BuildSettingExtractor menu to set generation options.
19 |
20 | ### Notes ###
21 |
22 | - BuildSettingExtractor does not alter the original Xcode project file.
23 | - BuildSettingExtractor does not update existing xcconfig files, it does a one-time extraction.
24 | - BuildSettingExtractor does not hoist shared target build settings to the project level.
25 | - Do not taunt BuildSettingExtractor.
26 |
27 | ### Generated Files ###
28 |
29 | The generated xcconfig files include build setting explanations gleaned from Xcode:
30 |
31 | // Framework Search Paths
32 | //
33 | // This is a list of paths to folders containing frameworks to be searched by the
34 | // compiler for both included or imported header files when compiling C, Objective-C,
35 | // C++, or Objective-C++, and by the linker for frameworks used by the product. Paths are
36 | // delimited by whitespace, so any paths with spaces in them need to be properly quoted.
37 | // [-F]
38 |
39 | FRAMEWORK_SEARCH_PATHS = $(DEVELOPER_FRAMEWORKS_DIR) $(inherited)
40 |
41 |
42 | // Info.plist File
43 | //
44 | // This is the project-relative path to the plist file that contains the Info.plist
45 | // information used by bundles.
46 |
47 | INFOPLIST_FILE = BuildSettingExtractorTests/BuildSettingExtractorTests-Info.plist
48 |
49 | These comments can be turned off in the Preferences window for a more compact file:
50 |
51 | FRAMEWORK_SEARCH_PATHS = $(DEVELOPER_FRAMEWORKS_DIR) $(inherited)
52 | INFOPLIST_FILE = BuildSettingExtractorTests/BuildSettingExtractorTests-Info.plist
53 |
54 | ### Version History ###
55 |
56 | *Version 1.4.8*
57 | *Dec 23, 2024*
58 |
59 | – Updated to extract build settings from Xcode 15.3-compatible project files.
60 | – Updated to extract build settings from Xcode 16.0-compatible project files.
61 | – Added new line to end of generated files.
62 | – Tested on macOS 15.1.1 Sequoia, Apple Silicon.
63 | – Built with Xcode 16.2 on macOS 15.1.1 Sequoia.
64 |
65 |
66 | *Version 1.4.7*
67 | *Mar 2, 2024*
68 |
69 | – Updated list of build setting info files for Xcode 15.3.
70 | – Tested on macOS 14.3.1 Sonoma, Apple Silicon.
71 | – Built with Xcode 15.2 on macOS 14.3.1 Sonoma.
72 |
73 |
74 | *Version 1.4.6*
75 | *Jun 12, 2023*
76 |
77 | – Updated to extract build settings from Xcode 15.0-compatible project files.
78 | – Updated list of build setting info files.
79 | – Tested on macOS 13.4 Ventura, Apple Silicon.
80 | – Built with Xcode 14.3 on macOS 12.4 Ventura.
81 |
82 |
83 | *Version 1.4.5*
84 | *Sep 13, 2022*
85 |
86 | – Updated to extract build settings from Xcode 14.0-compatible project files.
87 | – Added grouping to conditional build setting generation.
88 | – Updated URL in generated files.
89 | – Tested on macOS 12.5.1 Monterey, Apple Silicon.
90 | – Built with Xcode 14.0 on macOS 12.5.1 Monterey.
91 |
92 |
93 | *Version 1.4.4*
94 | *May 7, 2022*
95 |
96 | – Updated list of build setting info files for Xcode 13.3 and later.
97 | – Added backstop entry for SWIFT_VERSION build setting.
98 | – Tested on macOS 12.3.1 Monterey, Apple Silicon.
99 | – Built with Xcode 13.3.1 on macOS 12.3.1 Monterey.
100 |
101 |
102 | *Version 1.4.3*
103 | *Oct 21, 2021*
104 |
105 | – Updated to extract build settings from Xcode 13.0-compatible project files.
106 | – Tested on macOS macOS 11.6 Big Sur.
107 | – Tested on macOS 12.0.1 Big Sur, Apple Silicon.
108 | – Built with Xcode 12.5.1 on macOS 11.6 Big Sur.
109 |
110 |
111 | *Version 1.4.2*
112 | *Nov 14, 2020*
113 |
114 | – Added native Apple Silicon support.
115 | – Updated app icon to follow macOS 11 Big Sur guidelines.
116 | – Updated icons in preferences window for macOS 11 Big Sur.
117 | – Fixed row spacing issue when building with Xcode 12 and later.
118 | – Updated list of build setting description files for Metal compiler and linker.
119 | – Updated to extract build settings from Xcode 11.4-compatible project files.
120 | – Added documentation for the BuildSettingInfoSubpaths.plist file.
121 | – Updated copyright notice.
122 | – Tested on macOS 10.14.6 Mojave.
123 | – Tested on macOS 10.15.7 Catalina.
124 | – Tested on macOS 11.0.1 Big Sur, Apple Silicon.
125 | – Built with Xcode 12.2 on macOS 10.15.7 Catalina.
126 |
127 | *Version 1.4.1*
128 | *Aug 3, 2020*
129 |
130 | – Added ability to drag Xcode project to app icon to extract settings.
131 | – Added support for no word separator in generated file names.
132 | – Added validation for text fields that should never be empty.
133 | – Added mechanism for handling introduced build setting info files.
134 | – Updated list of build setting info files for Xcode 12.0 beta.
135 | – Updated to extract build settings from Xcode 12.0-compatible project files.
136 | – Convert app scheme to use a test plan.
137 | – Tested on macOS 10.15.5 Catalina.
138 | – Tested on macOS 11.0 Big Sur beta 3.
139 | – Built with Xcode 11.6 on macOS 10.15.5 Catalina.
140 |
141 | *Version 1.4*
142 | *May 17, 2020*
143 |
144 | – Moved preferences from a sheet to a separate window.
145 | – Added preference to generate xcconfig files in folders grouped by target.
146 | – Added preference to group project xcconfig files in a folder.
147 | – Added preference to set name of folder enclosing all generated xcconfig files.
148 | – Folder enclosing generated xcconfig files is named according to preference value.
149 | – Added preference to automatically save generated files in same folder as source project.
150 | – Added preference for line spacing between settings.
151 | – Added preference for aligning build setting values.
152 | – Added a preview to each preference pane.
153 | – Added Close command to the File menu.
154 | – Added shared xcscheme file to allow Xcode Server usage.
155 | – Updated list of build setting info files for Xcode 11.4.
156 | – Updated minimum macOS deployment target to 10.14.
157 | – Tested on macOS 10.15.4 Catalina.
158 | – Built with Xcode 11.4.1 on macOS 10.15.4 Catalina.
159 |
160 | *Version 1.3.2*
161 | *Oct 13, 2019*
162 |
163 | – Spaces are now replaced with separator in generated xcconfig file names.
164 | – Added BSE-CodeSigning.xcconfig file to store easily accessible code signing settings.
165 | – Enabled hardened runtime to allow app notarization.
166 | – Enabled sandboxing with entitlement for read-write access to user-selected files.
167 | – Added support for Xcode beta as build setting info souce.
168 | – Added error reporting if no build setting info source is found.
169 | – Refactored error creation, handling and presentation.
170 | – Refactored AppKit-dependent category methods into separate file.
171 | – Removed precompiled headers.
172 | – Moved to explicit @import statements.
173 | – Tested on macOS 10.15 Catalina.
174 | – Built with Xcode 11.1 on macOS 10.14.6 Mojave.
175 |
176 | *Version 1.3.1*
177 | *Sep 15, 2019*
178 |
179 | – Updated to extract build settings from Xcode 11.0-compatible project files.
180 | – Add mechanism for handling deprecated build setting info files.
181 | – Update list of build setting info files for Xcode 11.
182 | – Add test to ensure build setting info files load without error.
183 | – Process Markdown in build setting options to plain text.
184 | – Tested on macOS 10.14.6 with Xcode 11.
185 | – Tested on macOS 10.15 Catalina beta 8.
186 | – Built with Xcode 11.0 on macOS 10.14.6 Mojave.
187 | - NOTE: The Xcode project file refuses to open in Xcode 11 GMc on macOS 10.15 Catalina beta 8.
188 |
189 | *Version 1.3*
190 | *Sep 25, 2018*
191 |
192 | – Added Dark Mode support.
193 | – Built with Xcode 10.0 on macOS 10.14 Mojave.
194 |
195 | *Version 1.2.8*
196 | *Sep 24, 2018*
197 |
198 | – Updated list of build setting description files to include Apple Clang file.
199 | – Fixed crash when xcspec file was not found when reading build setting descriptions.
200 | – Tested Xcode 10.0-compatible project files with Xcode 10.
201 | – Tested build setting descriptions with Xcode 10.
202 | – This the last version that will build cleanly on macOS 10.13 and earlier.
203 | – Built with Xcode 9.4.1 on macOS 10.13.6 High Sierra.
204 |
205 | *Version 1.2.7*
206 | *Aug 19, 2018*
207 |
208 | – Added alert to notify users if the selected project contains no build settings.
209 | – Added names and descriptions for some common settings without info.
210 | – Updated to extract build settings from Xcode 9.3-compatible project files.
211 | – Updated to extract build settings from Xcode 10.0-compatible project files. (Tested with beta 6.)
212 | – Made changes to prepare for Mojave Dark Mode.
213 |
214 | *Version 1.2.6*
215 | *Nov 3, 2017*
216 |
217 | – Added menu item to choose Xcode project.
218 | – Removed unused menu items.
219 | – Added window minimum size.
220 | – Updated window with options more befitting a single-window app.
221 | – Improved support for reading build setting descriptions from xcspec files.
222 | – Updated list of build setting description files to include Swift xcspec file.
223 |
224 | *Version 1.2.5*
225 | *Jun 13, 2017*
226 |
227 | – Resolve possible naming conflict between generated project and target files.
228 | – Updated list of build setting description files to include LLDB 8.1 file.
229 | – Updated list of build setting description files to include LLDB 9.0 file.
230 | – Fixed incorrect Markdown in the ReadMe file.
231 |
232 | *Version 1.2.4*
233 | *Nov 14, 2016*
234 |
235 | – Updated list of build setting description files to include LLDB 7.1 file.
236 | – Updated list of build setting description files to include LLDB 8.0 file.
237 | – Added support for reading build setting descriptions from xcspec files.
238 | – Updated to extract build settings from Xcode 6.3-compatible project files.
239 | – Updated to extract build settings from Xcode 8.0-compatible project files.
240 |
241 | *Version 1.2.3*
242 | *Nov 9, 2015*
243 |
244 | – Updated list of build setting description files to include new LLDB 7.0 file.
245 | – No longer inexplicably using return instead of newline character in two spots.
246 |
247 | *Version 1.2.2*
248 | *May 25, 2015*
249 |
250 | – Added brand new app icon.
251 | – Removed default Credits.rtf file.
252 |
253 | *Version 1.2.1*
254 | *May 16, 2015*
255 |
256 | – Updated list of build setting description files to include new LLDB 6.1 file.
257 |
258 | *Version 1.2*
259 | *May 16, 2015*
260 |
261 | – Added options for generated file names in Preferences. (Thank you [Alex Curylo](https://github.com/alexcurylo)!)
262 |
263 | *Version 1.1.1*
264 | *May 12, 2015*
265 |
266 | – Fixed crash on Mavericks.
267 |
268 | *Version 1.1*
269 | *February 7, 2015*
270 |
271 | – Added build settings explaination comments gleaned from Xcode.
272 | – Files are shown in Finder after they are generated.
273 | – Both options can be turned off in new Preferences pane.
274 | – Extraction and file generation now occurs in the background.
275 |
276 | *Version 1.0*
277 | *January 31, 2015*
278 |
279 | – Initial version of BuildSettingExtractor.
280 | – Generates xcconfig files from the build settings in an Xcode project.
281 |
282 | *****
283 |
284 | *This code is provide as-is with no warranties express or implied.
285 | Please put projects in source control to guard against things going horribly awry.*
286 |
--------------------------------------------------------------------------------
/ci_scripts/ci_post_clone.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # ci_post_clone.sh
4 | # BuildSettingExtractor
5 | #
6 | # Created by James Dempsey on 6/10/23.
7 | #
8 |
9 | if [ $CI_XCODE_CLOUD = "TRUE" ]; then
10 |
11 | # Get path to xcconfig files in project
12 | configFolderPath = "$CI_PRIMARY_REPOSITORY_PATH/Config"
13 |
14 | # Write private project configuration file
15 | privateProjectConfigPath="$configFolderPath/PrivateProjectConfig.xcconfig"
16 |
17 | cat > $privateProjectConfigPath <<- EOF
18 | DEVELOPMENT_TEAM = $CI_TEAM_ID
19 | EOF
20 |
21 | # Write private app configuration file
22 | privateAppConfigPath="$configFolderPath/PrivateAppConfig.xcconfig"
23 |
24 | cat > $privateAppConfigPath <<- EOF
25 | PRODUCT_BUNDLE_IDENTIFIER = $CI_BUNDLE_ID
26 | EOF
27 |
28 | else
29 |
30 | echo "CI_XCODE_CLOUD env variable was not TRUE"
31 |
32 | fi
33 |
--------------------------------------------------------------------------------