1+ /*
2+ Copyright (C) 2025 Free Software Foundation, Inc.
3+
4+ Written by: GitHub Copilot <[email protected] > 5+ Date: 2025 Oct 23
6+
7+ This file is part of the GNUstep XCode Library
8+
9+ This library is free software; you can redistribute it and/or
10+ modify it under the terms of the GNU Lesser General Public
11+ License as published by the Free Software Foundation; either
12+ version 2 of the License, or (at your option) any later version.
13+
14+ This library is distributed in the hope that it will be useful,
15+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+ Lesser General Public License for more details.
18+
19+ You should have received a copy of the GNU Lesser General Public
20+ License along with this library; if not, write to the Free
21+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22+ Boston, MA 02110 USA.
23+ */
24+
25+ #import " GSXCProjectBuilderGenerator.h"
26+ #import " PBXNativeTarget.h"
27+ #import " XCConfigurationList.h"
28+ #import " PBXBuildFile.h"
29+ #import " PBXFileReference.h"
30+
31+ #import " NSArray+Additions.h"
32+
33+ @implementation GSXCProjectBuilderGenerator
34+
35+ - (NSString *) projectTypeForString : (NSString *)type
36+ {
37+ if ([type isEqualToString: @" APPLICATION" ])
38+ {
39+ return @" Application" ;
40+ }
41+ else if ([type isEqualToString: @" BUNDLE" ])
42+ {
43+ return @" Bundle" ;
44+ }
45+ else if ([type isEqualToString: @" FRAMEWORK" ])
46+ {
47+ return @" Framework" ;
48+ }
49+ else if ([type isEqualToString: @" LIBRARY" ])
50+ {
51+ return @" Library" ;
52+ }
53+ else if ([type isEqualToString: @" TOOL" ])
54+ {
55+ return @" Tool" ;
56+ }
57+ return type;
58+ }
59+
60+ - (id ) objectForString : (NSString *)o
61+ {
62+ return o != nil ? o : @" " ;
63+ }
64+
65+ - (BOOL ) arrayHasValidContent : (NSArray *)array
66+ {
67+ if (array == nil || [array count ] == 0 )
68+ {
69+ return NO ;
70+ }
71+
72+ // Check if all elements are non-empty strings
73+ NSEnumerator *en = [array objectEnumerator ];
74+ id obj = nil ;
75+ while ((obj = [en nextObject ]) != nil )
76+ {
77+ if (![obj isKindOfClass: [NSString class ]] ||
78+ [(NSString *)obj length ] == 0 )
79+ {
80+ continue ; // Skip empty strings but don't reject the whole array
81+ }
82+ return YES ; // Found at least one valid string
83+ }
84+
85+ return NO ; // No valid strings found
86+ }
87+
88+ - (NSString *) safeStringFromArray : (NSArray *)array withMethod : (SEL )method
89+ {
90+ if (![self arrayHasValidContent: array])
91+ {
92+ return @" " ;
93+ }
94+
95+ NSString *result = [array performSelector: method];
96+ return result != nil ? result : @" " ;
97+ }
98+
99+ - (BOOL ) generate
100+ {
101+ BOOL result = YES ;
102+ GSXCBuildContext *context = [GSXCBuildContext sharedBuildContext ];
103+ NSString *name = [_target name ];
104+ NSString *appName = [name stringByDeletingPathExtension ];
105+ NSString *projectFileName = @" PB.project" ;
106+ NSMutableDictionary *projectDict = [NSMutableDictionary dictionary ];
107+ NSMutableDictionary *filesTable = [NSMutableDictionary dictionary ];
108+
109+ NSArray *objCFiles = [context objectForKey: @" OBJC_FILES" ];
110+ NSArray *cFiles = [context objectForKey: @" C_FILES" ];
111+ NSArray *cppFiles = [context objectForKey: @" CPP_FILES" ];
112+ NSArray *objCPPFiles = [context objectForKey: @" OBJCPP_FILES" ];
113+ NSArray *headerFiles = [context objectForKey: @" HEADERS" ];
114+ NSArray *resourceFiles = [context objectForKey: @" RESOURCES" ];
115+ NSArray *otherSources = [context objectForKey: @" OTHER_SOURCES" ];
116+ NSArray *libraries = [context objectForKey: @" ADDITIONAL_OBJC_LIBS" ];
117+ NSString *projectType = [context objectForKey: @" PROJECT_TYPE" ];
118+
119+ // Debug output to see what we're getting from the context
120+ NSDebugLog (@" === DEBUG: ProjectBuilder Generator Context ===" );
121+ NSDebugLog (@" OBJC_FILES: %@ " , objCFiles);
122+ NSDebugLog (@" C_FILES: %@ " , cFiles);
123+ NSDebugLog (@" CPP_FILES: %@ " , cppFiles);
124+ NSDebugLog (@" OBJCPP_FILES: %@ " , objCPPFiles);
125+ NSDebugLog (@" HEADERS: %@ " , headerFiles);
126+ NSDebugLog (@" RESOURCES: %@ " , resourceFiles);
127+ NSDebugLog (@" OTHER_SOURCES: %@ " , otherSources);
128+ NSDebugLog (@" LIBRARIES: %@ " , libraries);
129+ NSDebugLog (@" PROJECT_TYPE: %@ " , projectType);
130+ NSDebugLog (@" ==============================================" );
131+
132+ // Construct the ProjectBuilder project structure
133+ xcputs (" \t * Generating ProjectBuilder project" );
134+
135+ // Set basic project information
136+ [projectDict setObject: appName forKey: @" PROJECTNAME" ];
137+ [projectDict setObject: [self projectTypeForString: [projectType uppercaseString ]] forKey: @" PROJECTTYPE" ];
138+
139+ // Combine all class files (source code files)
140+ NSMutableArray *classFiles = [NSMutableArray array ];
141+ if ([self arrayHasValidContent: objCFiles])
142+ {
143+ [classFiles addObjectsFromArray: objCFiles];
144+ }
145+ if ([self arrayHasValidContent: cFiles])
146+ {
147+ [classFiles addObjectsFromArray: cFiles];
148+ }
149+ if ([self arrayHasValidContent: cppFiles])
150+ {
151+ [classFiles addObjectsFromArray: cppFiles];
152+ }
153+ if ([self arrayHasValidContent: objCPPFiles])
154+ {
155+ [classFiles addObjectsFromArray: objCPPFiles];
156+ }
157+
158+ // Build the FILESTABLE dictionary structure (as used by ProjectBuilder)
159+ if ([classFiles count ] > 0 )
160+ {
161+ [filesTable setObject: classFiles forKey: @" CLASSES" ];
162+ }
163+
164+ if ([self arrayHasValidContent: headerFiles])
165+ {
166+ [filesTable setObject: headerFiles forKey: @" H_FILES" ];
167+ }
168+
169+ if ([self arrayHasValidContent: resourceFiles])
170+ {
171+ // ProjectBuilder uses INTERFACES for NIB/interface files, but we'll put resources here
172+ [filesTable setObject: resourceFiles forKey: @" INTERFACES" ];
173+ }
174+
175+ if ([self arrayHasValidContent: otherSources])
176+ {
177+ [filesTable setObject: otherSources forKey: @" OTHER_LINKED" ];
178+ }
179+
180+ if ([self arrayHasValidContent: libraries])
181+ {
182+ // Convert library flags to just library names
183+ NSMutableArray *cleanLibraries = [NSMutableArray array ];
184+ NSEnumerator *en = [libraries objectEnumerator ];
185+ NSString *lib = nil ;
186+
187+ while ((lib = [en nextObject ]) != nil )
188+ {
189+ if ([lib hasPrefix: @" -l" ])
190+ {
191+ [cleanLibraries addObject: [lib substringFromIndex: 2 ]];
192+ }
193+ else
194+ {
195+ [cleanLibraries addObject: lib];
196+ }
197+ }
198+
199+ if ([cleanLibraries count ] > 0 )
200+ {
201+ [filesTable setObject: cleanLibraries forKey: @" FRAMEWORKS" ];
202+ }
203+ }
204+
205+ // Add empty arrays for any missing required ProjectBuilder sections
206+ if ([filesTable objectForKey: @" CLASSES" ] == nil )
207+ {
208+ [filesTable setObject: [NSArray array ] forKey: @" CLASSES" ];
209+ }
210+ if ([filesTable objectForKey: @" H_FILES" ] == nil )
211+ {
212+ [filesTable setObject: [NSArray array ] forKey: @" H_FILES" ];
213+ }
214+ if ([filesTable objectForKey: @" INTERFACES" ] == nil )
215+ {
216+ [filesTable setObject: [NSArray array ] forKey: @" INTERFACES" ];
217+ }
218+ if ([filesTable objectForKey: @" OTHER_LINKED" ] == nil )
219+ {
220+ [filesTable setObject: [NSArray array ] forKey: @" OTHER_LINKED" ];
221+ }
222+ if ([filesTable objectForKey: @" FRAMEWORKS" ] == nil )
223+ {
224+ [filesTable setObject: [NSArray array ] forKey: @" FRAMEWORKS" ];
225+ }
226+ if ([filesTable objectForKey: @" IMAGES" ] == nil )
227+ {
228+ [filesTable setObject: [NSArray array ] forKey: @" IMAGES" ];
229+ }
230+
231+ // Set the FILESTABLE in the main project dictionary
232+ [projectDict setObject: filesTable forKey: @" FILESTABLE" ];
233+
234+ // Add ProjectBuilder-specific metadata
235+ [projectDict setObject: @" 2.1" forKey: @" LANGUAGE" ];
236+ [projectDict setObject: @" NeXT Project Builder Project v2.1" forKey: @" NEXTSTEP_BUILDTOOL" ];
237+
238+ NSDebugLog (@" ProjectBuilder project dict = %@ " , projectDict);
239+
240+ // Write the PB.project file
241+ if (![projectDict writeToFile: projectFileName atomically: YES ])
242+ {
243+ xcprintf (" Error writing project file %s " , [projectFileName cString ]);
244+ return NO ;
245+ }
246+
247+ xcputs ([[NSString stringWithFormat: @" === Completed generation of %@ for target %@ " , projectFileName, name] cString ]);
248+
249+ return result;
250+ }
251+
252+ @end
0 commit comments