Skip to content

Commit 01f91a4

Browse files
committed
Create a safe copy of the app icon
1 parent 4d6dcb5 commit 01f91a4

File tree

4 files changed

+153
-3
lines changed

4 files changed

+153
-3
lines changed

Generators/Makefile/GSXCMakefileGenerator.m

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ - (BOOL) generate
7575
NSString *additionalIncludes = [self safeStringFromArray: [context objectForKey: @"ADDITIONAL_INCLUDE_DIRS"] withMethod: @selector(arrayToIncludeList)];
7676
NSString *additionalOCflags = [self safeStringFromArray: [context objectForKey: @"ADDITIONAL_OBJC_LIBS"] withMethod: @selector(arrayToLinkList)];
7777
NSString *projectType = [context objectForKey: @"PROJECT_TYPE"];
78+
79+
// Get app icon information from build context
80+
NSString *appIconFile = [context objectForKey: @"APP_ICON_FILE"];
81+
NSString *safeAppIconFile = [context objectForKey: @"APP_ICON_SAFE_FILE"];
7882

7983
// Debug output to see what we're getting from the context
8084
NSDebugLog(@"=== DEBUG: Makefile Generator Context ===");
@@ -84,6 +88,8 @@ - (BOOL) generate
8488
NSDebugLog(@"OBJCPP_FILES: %@", [context objectForKey: @"OBJCPP_FILES"]);
8589
NSDebugLog(@"RESOURCES: %@", [context objectForKey: @"RESOURCES"]);
8690
NSDebugLog(@"PROJECT_TYPE: %@", projectType);
91+
NSDebugLog(@"APP_ICON_FILE: %@", appIconFile);
92+
NSDebugLog(@"APP_ICON_SAFE_FILE: %@", safeAppIconFile);
8793
NSDebugLog(@"========================================");
8894

8995
// Construct the makefile out of the data we have thusfar collected.
@@ -143,6 +149,23 @@ - (BOOL) generate
143149
[NSString stringWithFormat: @"%@_RESOURCE_FILES = %@\n\n", appName, resourceFilesString]];
144150
}
145151

152+
// Add app icon handling if an icon was discovered
153+
if (appIconFile != nil && [projectType isEqualToString: @"application"])
154+
{
155+
NSString *iconToUse = safeAppIconFile ? safeAppIconFile : appIconFile;
156+
makefileString = [makefileString stringByAppendingString: @"# App icon handling\n"];
157+
makefileString = [makefileString stringByAppendingString:
158+
[NSString stringWithFormat: @"%@_MAIN_MODEL_FILE = %@\n", appName, iconToUse]];
159+
160+
// Add a rule to copy the app icon to the app bundle
161+
makefileString = [makefileString stringByAppendingString: @"# Copy app icon to Resources directory\n"];
162+
makefileString = [makefileString stringByAppendingString:
163+
[NSString stringWithFormat: @"after-install::\n\t$(INSTALL_DATA) %@ $(GNUSTEP_APP_INSTALL_DIR)/%@.app/Resources/\n\n",
164+
iconToUse, appName]];
165+
166+
xcputs([[NSString stringWithFormat: @"\t* Added app icon to makefile: %@", iconToUse] cString]);
167+
}
168+
146169
if ([additionalIncludes length] > 0)
147170
{
148171
makefileString = [makefileString stringByAppendingString:

XCode/PBXResourcesBuildPhase.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@
5555
*/
5656
- (BOOL) copyAppIconToResources: (NSString *)iconFilename;
5757

58+
/**
59+
* Creates a safe copy of the app icon file with spaces replaced by underscores.
60+
* If the icon filename contains spaces, creates a copy with a makefile-safe name.
61+
* Takes the name of the icon file to process as parameter.
62+
* Returns the safe filename if a copy was created, or nil if no spaces were found.
63+
*/
64+
- (NSString *) createSafeIconCopy: (NSString *)iconFilename;
65+
5866
/**
5967
* Escapes special characters in filenames for makefile compatibility.
6068
* Handles spaces, dollar signs, hash symbols, and colons that have special

XCode/PBXResourcesBuildPhase.m

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,46 @@ - (BOOL) copyAppIconToResources: (NSString *)iconFilename
238238
return success;
239239
}
240240

241+
- (NSString *) createSafeIconCopy: (NSString *)iconFilename
242+
{
243+
if (iconFilename == nil || [iconFilename rangeOfString: @" "].location == NSNotFound)
244+
{
245+
// No spaces, return original filename
246+
return nil;
247+
}
248+
249+
NSFileManager *mgr = [NSFileManager defaultManager];
250+
NSString *productName = [_target name];
251+
NSString *assetsDir = [productName stringByAppendingPathComponent: @"Assets.xcassets"];
252+
NSString *appIconDir = [assetsDir stringByAppendingPathComponent: @"AppIcon.appiconset"];
253+
NSString *sourcePath = [appIconDir stringByAppendingPathComponent: iconFilename];
254+
255+
// Create safe filename by replacing spaces with underscores
256+
NSString *safeFilename = [iconFilename stringByReplacingOccurrencesOfString: @" " withString: @"_"];
257+
NSString *safePath = [appIconDir stringByAppendingPathComponent: safeFilename];
258+
259+
xcputs([[NSString stringWithFormat: @"\t* Creating safe icon copy: %@ -> %@", iconFilename, safeFilename] cString]);
260+
261+
// Copy original to safe filename
262+
NSError *error = nil;
263+
if ([mgr fileExistsAtPath: safePath])
264+
{
265+
[mgr removeItemAtPath: safePath error: NULL];
266+
}
267+
268+
BOOL success = [mgr copyItemAtPath: sourcePath toPath: safePath error: &error];
269+
if (success)
270+
{
271+
xcputs([[NSString stringWithFormat: @"\t* Successfully created safe icon copy: %@", safeFilename] cString]);
272+
return safeFilename;
273+
}
274+
else
275+
{
276+
xcputs([[NSString stringWithFormat: @"\t* ERROR: Failed to create safe icon copy: %@", error ? [error localizedDescription] : @"Unknown error"] cString]);
277+
return nil;
278+
}
279+
}
280+
241281
- (NSString *) processAssets
242282
{
243283
NSString *filename = [self discoverAppIcon];
@@ -625,18 +665,32 @@ - (BOOL) generate
625665
NSString *iconPath = [self discoverAppIconPath];
626666
xcputs([[NSString stringWithFormat: @"\t* Discovered app icon: %@", iconFile ? iconFile : @"(none)"] cString]);
627667

668+
// Store app icon information in build context for makefile generation
669+
GSXCBuildContext *context = [GSXCBuildContext sharedBuildContext];
670+
if (iconFile != nil)
671+
{
672+
[context setObject: iconFile forKey: @"APP_ICON_FILE"];
673+
xcputs([[NSString stringWithFormat: @"\t* Stored app icon in build context: %@", iconFile] cString]);
674+
}
675+
628676
// Add icon file to resources if found
629677
if (iconPath != nil)
630678
{
631679
NSString *escapedIconPath = [self escapeFilenameForMakefile: iconPath];
632680
xcputs([[NSString stringWithFormat: @"\t* Adding app icon to resources: %@", iconPath] cString]);
633681
[resources addObject: escapedIconPath];
634682

635-
// Copy app icon to resources directory
636-
BOOL iconCopied = [self copyAppIconToResources: iconFile];
683+
// Copy app icon to resources directory with safe filename
684+
NSString *safeIconFile = [self createSafeIconCopy: iconFile];
685+
BOOL iconCopied = [self copyAppIconToResources: safeIconFile ? safeIconFile : iconFile];
637686
if (iconCopied)
638687
{
639-
xcputs([[NSString stringWithFormat: @"\t* Copied app icon to resources: %@", iconFile] cString]);
688+
xcputs([[NSString stringWithFormat: @"\t* Copied app icon to resources: %@", safeIconFile ? safeIconFile : iconFile] cString]);
689+
// Store the safe filename for makefile generation
690+
if (safeIconFile != nil)
691+
{
692+
[context setObject: safeIconFile forKey: @"APP_ICON_SAFE_FILE"];
693+
}
640694
}
641695
else
642696
{

test_app_icon.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# App Icon Handling Implementation
2+
3+
## Summary of Changes
4+
5+
The implementation adds automatic discovery and handling of application icons when generating makefiles, with special handling for filenames containing spaces.
6+
7+
## Files Modified:
8+
9+
### 1. PBXResourcesBuildPhase.h
10+
- Added `createSafeIconCopy:` method declaration
11+
12+
### 2. PBXResourcesBuildPhase.m
13+
- Modified icon discovery to store information in GSXCBuildContext
14+
- Added `createSafeIconCopy:` method to handle filenames with spaces
15+
- Enhanced icon copying to create safe copies when needed
16+
17+
### 3. GSXCMakefileGenerator.m
18+
- Enhanced array checking to prevent makefile cruft
19+
- Added app icon retrieval from build context
20+
- Added app icon handling in generated makefiles
21+
- Added debug output for app icon information
22+
23+
## Key Features:
24+
25+
### 1. Space Handling
26+
- **Problem**: Icon filenames with spaces cause issues in makefiles
27+
- **Solution**: Creates safe copies with underscores replacing spaces
28+
- **Example**: "My App Icon.png" → "My_App_Icon.png"
29+
30+
### 2. Makefile Generation
31+
When an app icon is discovered, the generated makefile includes:
32+
33+
```makefile
34+
# App icon handling
35+
MyApp_MAIN_MODEL_FILE = My_App_Icon.png
36+
37+
# Copy app icon to Resources directory
38+
after-install::
39+
$(INSTALL_DATA) My_App_Icon.png $(GNUSTEP_APP_INSTALL_DIR)/MyApp.app/Resources/
40+
```
41+
42+
### 3. Build Context Integration
43+
- App icon information stored in GSXCBuildContext
44+
- Keys: `APP_ICON_FILE` (original) and `APP_ICON_SAFE_FILE` (safe copy)
45+
- Accessible across build phases and generators
46+
47+
### 4. Automatic Processing
48+
- Icon discovery happens during resource build phase
49+
- Safe copies created automatically if needed
50+
- Makefile generation includes icon handling without manual configuration
51+
52+
## Usage:
53+
54+
1. Place app icons in `Assets.xcassets/AppIcon.appiconset/`
55+
2. Ensure `Contents.json` defines a 32x32@1x icon
56+
3. Run buildtool - icon handling is automatic
57+
4. Generated makefile will include proper icon installation rules
58+
59+
## Benefits:
60+
61+
- **Eliminates manual icon handling**: No need to manually copy icons or add makefile rules
62+
- **Handles spaces properly**: Automatically creates makefile-safe filenames
63+
- **Cross-platform compatibility**: Works with GNUstep makefiles
64+
- **Debug visibility**: Clear logging of icon processing steps
65+
- **Robust error handling**: Graceful fallbacks when icons are not found

0 commit comments

Comments
 (0)