Menu

[r129]: / APIInterface / Docs / compilers-linkers.html  Maximize  Restore  History

Download this file

416 lines (348 with data), 20.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
<html>
<head>
<title>Xcode's Plugin Interface : Compilers & Linkers</title>
<link rel="stylesheet" href="doc.css" />
</head>
<body>
<h1>Xcode's Plugin Interface : Compilers & Linkers</h1>
<!-- --- --- --- --- -->
<h2>Table of Contents</h2>
<div class="toc">
<div class="toc1"><a href="index.html">UP</a> <small>(Xcode's Plugin Interface)</small></div>
<div class="toc1"><a href="#buildsystem">1. Xcode's Build System</a></div>
<div class="toc1"><a href="#api">2. Graph Creation API</a></div>
<div class="toc1"><a href="#compiler">3. Compiler Sample Code (<code>.pbcompspec</code>)</a></div>
<div class="toc1"><a href="#linker">4. Linker Sample Code (<code>.pblinkspec</code>)</a></div>
<div class="toc1"><a href="#outputparsing">5. Parsing the ouput of command line tools</a> <small>(TODO)</small></div>
<div class="toc1"><a href="#producttype">6. Product Type Sample Code (<code>.pbprodspec</code>)</a></div>
</div>
<!-- --- --- --- --- -->
<h2><a name="buildsystem">1. Xcode's Build System</a></h2>
<p>Everything starts when Xcode loads your plugin bundle, just after you launch it. Xcode looks at
the content of the <code>Info.plist</code> file to initialize your plugin, and read all
<code>*.pb*spec</code> or <code>.xcspec</code> files of your plugin <code>Resources</code> folder.
At this time, Xcode don't execute any code, except if you add a <code>NSPrincipalClass</code> entry
in the <code>Info.plist</code> file, it just stores informations about what provide your plugin.</p>
<p>The second phase, the most important one, is the dependency graph creation. It appends every
time you open a project, add/remove files to/from a project, modify some build settings&hellip; It's the
phase where your plugin code is run.</p>
<p>Your code must create one <code>XCDependencyNode</code> for each file involved in the compilation
(source file, object files, final or product files), and add dependency relations between each nodes.
Some of these nodes will be marked as "product nodes" by calling <code>addProductNode:</code>, nodes
representing a file of your final product. Nodes are identified by a name which is, by convention,
the POSIX path of the corresponding file.</p>
<p>Here is the description of how is run the dependency creation phase :</p>
<ul>
<li><strong>Xcode creates a <code>PBXTargetBuildContext</code> object</strong>. This object will
be passed as parameter to all your fonctions. You must use it to modify/expand environment
variables, modify the dependency graph and create commands.</li>
<li>Xcode looks at the current target to know what are the product file created. All informations
attached to a target are stored inside the project file. When you add a new target to a project,
Xcode copies the content of the corresponding <code>.trgttmpl</code> file (in
<code>Library/Application Support/Apple/Developer Tools/Target Templates/</code>) to the project.</li>
<li>In the target description, there're references to all product files (files that are contained in
your final product). The product files are described in <code>.pbprodspec</code> and <code>.pbpackspec</code>
files. I did not really understood the differences between these two specification types, except
that they are strongly related.</li>
<li><strong>If you've created a custom <code>XCProductTypeSpecification</code> class, Xcode calls
it's <code>computeProductDependencies&hellip;:</code> method</strong> : it must contains code to
define product nodes.</li>
<li>Then, Xcode lists all source files (from the "source build phase" of the target). It looks
at the complier rules (described in <code>.buildrules</code> files, or created by the user in
the "Rules" tab of the target information window) to find which compiler must be used to compile
the source file. <strong>Xcode now call for each source file the <code>computeDependenciesForFilePath&hellip;</code>
method of the appropriated compiler</strong> (an object of a class inherited from
<code>XCCompilerSpecification</code>). The code of this method must create dependency links
between source and compiled file, add the compiled file to the <code>object_files_$(variant)_$(arch)</code>
environment variable, create the command which will be used to compile the source file, and
return the list of output nodes created by this command.</li>
<li>If there're source files the returned files, Xcode automatically send it to the appropriated
compiler. It may be useful for preprocessor commands like lex and yacc.</li>
<li><strong>Now, Xcode enters the linking phase.</strong> It first needs to know which linker to
use, so it calls the <code>linkerSpecificationForObjectFiles&hellip;</code> method of the product
specification object (inherited from the <code>XCProductTypeSpecification</code> class). If a
linker is specified by the <code>compiler_mandated_linker</code> variable, Xcode use it instead of
the linker returned by <code>linkerSpecificationForObjectFiles&hellip;</code>. You may also set
the key <code>RequiredLinker</code> in a compiler specification to override the value defined by
the product specification. Then, Xcode calls the <code>computeDependenciesForFilePaths&hellip;</code>
of this linker : this method must create dependency links between product nodes and object files,
the command to be runned to call the linker, and return the list of files created by the linker.</li>
</ul>
<p>The last phase is the real compilation/linking of files, the building phase. Here, Xcode read the
dependency graph and run the command only if they are needed (i.e. if the output file doesn't exist
or if the input files have been modified since last compilation) and in an best order that satisfy
the dependencies.</p>
<p>(only for Xcode 2.1) During the building phase, Xcode create <code>.dot</code> files describing the dependency graph
after and before running the commands (you'll find them in <code>build/«project».build/«configuration»/«target».build/</code>).
You need the <a href="http://www.pixelglow.com/graphviz/">Graphviz</a> application to view them.
These .dot files are very useful to check if your code create the good dependency graph.</p>
<!-- --- --- --- --- -->
<h2><a name="api">2. Graph Creation API</a></h2>
<p>Xcode methods called by your plugin :</p>
<ul>
<li>Node creation (name is the file path) :<br />
<code>-[PBXTargetBuildContext dependencyNodeForName:createIfNeeded:]</code></li>
<li>Mark a node as "product node" :<br />
<code>-[PBXTargetBuildContext addProductNode:]</code></li>
<li>Create command :<br />
<code>-[PBXTargetBuildContext createCommandWithRuleInfo:commandPath:arguments:forNode:]</code></li>
<li>Dependency definition :<br />
<code>-[XCDependencyNode addDependedNode:]</code> (must be called after creating a dependency
command for the output file) <br />
<code>-[XCDependencyNode addIncludeNode:]</code> (only for included header files)</li>
<li>Add source &lt;-&gt; compiled file links :<br />
<code>-[PBXTargetBuildContext setCompiledFilePath:forSourceFilePath:]</code></li>
</ul>
<p>Methods of your plugin called by Xcode :</p>
<ul>
<li>Create product nodes :<br />
<code>-[MyProductTypeSpecification computeProductDependenciesInTargetBuildContext:]</code></li>
<li>Choose a linker :<br />
<code>-[MyProductTypeSpecification linkerSpecificationForObjectFilesInTargetBuildContext:]</code></li>
<li>Create compilation rules (called for each source file) :<br />
<code>-[MyCompilerSpecification computeDependenciesForFilePath:ofType:outputDirectory:inTargetBuildContext:]</code></li>
<li>Create linking rules :<br />
<code>-[MyLinkerSpecification computeDependenciesForFilePaths:outputPath:inTargetBuildContext:]</code></li>
</ul>
<p>For more informations, see the <code>XCPSpecifications.h</code>, <code>XCPBuildSystem.h</code> and
<code>XCPDependencyGraph.h</code> header files available <a href="xcode-plugin-headers.zip">here</a>.</p>
<!-- --- --- --- --- -->
<h2><a name="compiler">3. Compiler Sample Code (<code>.pbcompspec</code>)</a></h2>
<h3>First solution : no code, less generic</h3>
<p>I've never tried this solution myself, and, according to people trying to use it, it doesn't work.
It's here for the case where you've more chance than others ;).</p>
<div style="color: grey; font-size: small">
<p>Starting with Xcode version 2.3, it's possible to integrate a compiler, without writing any code.
All you have to do is to write a <code>mycompiler.pbcompspec</code> file with the following format :</p>
<pre>{
Identifier = com.domain.me.compiler;
Class = MyCompilerSpecification; // optional
CommandInvocationClass = MyToolInvocation; // optional
Name = "My Compiler";
Description = "My source compiler";
Version = "Default"; // optional
Vendor = "Me"; // optional
Architectures = ("ppc","i386"); // optional
IsArchitectureNeutral = No; // optional (if "Yes", "Architectures" is useless)
Languages = (mytype); // optional
FileTypes = (sourcecode.mytype);
ExecPath = "/usr/local/bin/mycompiler";
RuleName = ( "ProcessingXXX", "$(OutputFile)", "$(InputFile)" );
OutputFileSuffix = ".o";
RequiredLinker = com.domain.me.linker; // optional (linker that must be used if using this
// compiler for at least one source file)
RequiredLibrarian = ??; // optional
CommandOutputParser = (&hellip;); // optional
PatternsOfFlagsNotAffectingPrecomps = (&hellip;) // optional
OptionsForCommandLine = (&hellip;); // optional
Options = {
{ Name = Input;
Type = stringlist;
CommandLineArgs = ( "$(value)" );
InputDependencies = "$(Input)"; // add to list of files needed before running the compiler
IsCommandInput = YES;
},
{ Name = Output;
Type = stringlist;
CommandLineArgs = ( "-o", "$(value)" );
OutputDependencies = "$(Output)"; // add to list of files created by the compiler
IsCommandOutput = YES;
},
{ Name = "build_file_compiler_flags"; // compiler flags specific to this source file
Type = stringlist;
CommandLineArgs = { "" = (); "&lt;&lt;otherwise&gt;&gt;" = ( "$(value)" ); };
}
&hellip; // see <a href="build-settings.html#buildsettings">here</a> for more details on options
};
OptionCategories = {&hellip;}; // optional (see <a href="build-settings.html#buildsettings">here</a>)
}</pre>
<p>As you can see, Xcode creates to new environment variables <code>$(OutputFile)</code> and
<code>$(InputFile)</code>. The output file path is derived from the input path by replacing the
extension with the value of the <code>OutputFileSuffix</code> key.</p>
</div>
<h3>Second solution : with Objective-C code, more generic</h3>
<p>If the first solution don't allow you to do what you want, you need to write a custom compiler
specification class (inheriting from <code>XCCompilerSpecification</code>). In this case you need
to add the <code>Class</code> key, but <code>RuleName</code>, <code>OutputFileSuffix</code>,
<code>OutputFileSuffix</code> are not required. You also don't need to use the <code>InputFile</code>,
<code>OutputFile</code> and <code>build_file_compiler_flags</code> options.</p>
<p>Code file <code>MyCompilerSpecification.m</code> :</p>
<pre>#import "MyCompilerSpecification.h"
#import "XCPBuildSystem.h"
#import "XCPDependencyGraph.h"
#import "XCPSupport.h"
@implementation MyCompilerSpecification
- (NSArray*) computeDependenciesForInputFile:(NSString*)input ofType:(PBXFileType*)type
variant:(NSString*)variant architecture:(NSString*)arch
outputDirectory:(NSString*)outputDir
inTargetBuildContext:(PBXTargetBuildContext*)context
{
<strong>// compute input path (for variable substitution)</strong>
input = [context expandedValueForString:input];
<strong>// compute output path</strong>
NSString* output = [outputDir stringByAppendingPathComponent:[[input lastPathComponent] stringByDeletingPathExtension]];
output = [context expandedValueForString:output];
<strong>// create dependency nodes</strong>
XCDependencyNode* outputNode = [context dependencyNodeForName:output createIfNeeded:YES];
XCDependencyNode* inputNode = [context dependencyNodeForName:input createIfNeeded:YES];
<strong>// create compiler command</strong>
XCDependencyCommand* dep = [context
createCommandWithRuleInfo:[NSArray arrayWithObjects:@"MyCompile",[context naturalPathForPath:input],nil]
commandPath:[context expandedValueForString:[self path]]
arguments:nil
forNode:outputNode];
[dep setToolSpecification:self];
[dep addArgumentsFromArray:[self commandLineForAutogeneratedOptionsInTargetBuildContext:context]];
[dep addArgumentsFromArray:[[context expandedValueForString:@"$(build_file_compiler_flags)"] arrayByParsingAsStringList]];
[dep addArgument:@"-o"];
[dep addArgument:output];
[dep addArgument:input];
<strong>// create dependency rules</strong>
[outputNode addDependedNode:inputNode];
<strong>// update source-compiled links</strong>
[context setCompiledFilePath:output forSourceFilePath:input];
<strong>// add to the list of file for the linker (only for non-".o" objects)</strong>
//NSString* object_files_variant_arch = [context expandedValueForString:@"object_files"];
//[context appendStringOrStringListValue:output toDynamicSetting:object_files_variant_arch];
<strong>// return output object node (Xcode will automaticaly add .o files to $(object_files))</strong>
return [NSArray arrayWithObject:outputNode];
}
@end</pre>
<!-- --- --- --- --- -->
<h2><a name="linker">4. Linker Sample Code (<code>.pblinkspec</code>)</a></h2>
<p>Specification file <code>mylinker.pblinkspec</code> :</p>
<pre>{
Identifier = com.domain.me.linker;
Class = MyLinkerSpecification;
Name = "My linker";
Description = "My linker";
Version = "Default"; // optional
Vendor = "Me"; // optional
BinaryFormats = ("mach-o");
Architectures = (ppc);
ExecPath = "/usr/local/bin/mylinker";
InputFileTypes = (compiled.mach-o.objfile);
CommandOutputParser = (&hellip;);
PatternsOfFlagsNotAffectingPrecomps = (&hellip;) // optional
OptionsForCommandLine = (&hellip;); // optional (see <a href="build-settings.html#buildsettings">here</a>)
Options = {&hellip;}; // optional (see <a href="build-settings.html#buildsettings">here</a>)
OptionCategories = {&hellip;}; // optional (see <a href="build-settings.html#buildsettings">here</a>)
}</pre>
<p>Code file <code>MyLinkerSpecification.m</code> :</p>
<pre>#import "MyLinkerSpecification.h"
#import "XCPBuildSystem.h"
#import "XCPDependencyGraph.h"
@implementation MyLinkerSpecification
- (NSArray*)createCommandsInBuildContext:(PBXTargetBuildContext*)context
{
<strong>// this method must be override since Xcode 3.0 : the default implementation doesn't call
// computeDependenciesForFilePaths: anymore</strong>
NSArray* inputs = [[context expandedValueForString:@"$(Inputs)"] arrayByParsingAsStringList];
NSString* output = [context expandedValueForString:@"$(Output)"];
return [self computeDependenciesForFilePaths:inputs outputPath:output inTargetBuildContext:context];
}
- (NSArray*)computeDependenciesForFilePaths:(NSArray*)inputs
outputPath:(NSString*)output
inTargetBuildContext:(PBXTargetBuildContext*)context
{
<strong>// compute output path (for variable substitution)</strong>
output = [context expandedValueForString:output];
<strong>// create linker command</strong>
XCDependencyNode* outputNode = [context dependencyNodeForName:output createIfNeeded:YES];
XCDependencyCommand* dep = [context
createCommandWithRuleInfo:[NSArray arrayWithObjects:@"MyLink",[context naturalPathForPath:output],nil]
commandPath:[context expandedValueForString:[self path]]
arguments:nil
forNode:outputNode];
[dep setToolSpecification:self];
[dep addArgumentsFromArray:[self commandLineForAutogeneratedOptionsInTargetBuildContext:context]];
[dep addArgument:@"-o"];
[dep addArgument:output];
<strong>// some types</strong>
PBXFileType* myObjectFileType = [PBXFileType specificationForIdentifier:@"compiled.myobjfile"];
PBXFileType* myLibraryFileType = [PBXFileType specificationForIdentifier:@"compiled.mylibraryfile"];
<strong>// create dependency rules & command arguments for libraries</strong>
NSEnumerator* libraryEnum = [[context linkedLibraryPaths] objectEnumerator];
NSString* library;
while((library = [libraryEnum nextObject]) != nil) {
library = [context expandedValueForString:library];
PBXFileType* type = [PBXFileType fileTypeForFileName:[library lastPathComponent]];
if([type isKindOfSpecification:myLibraryFileType] || [type isKindOfSpecification:myObjectFileType]) {
XCDependencyNode* libraryNode = [context dependencyNodeForName:library createIfNeeded:YES];
[outputNode addDependedNode:libraryNode];
[dep addArgument:library];
} else {
[context addDependencyAnalysisWarningMessageFormat:
@"warning: skipping file '%@' (unexpected file type '%@' in Frameworks & Libraries build phase)",
library, [type identifier]];
}
}
<strong>// create dependency rules & command arguments for compiled object</strong>
NSEnumerator* objectEnum = [inputs objectEnumerator];
NSString* input;
while((input = [objectEnum nextObject]) != nil) {
input = [context expandedValueForString:input];
PBXFileType* type = [PBXFileType fileTypeForFileName:[input lastPathComponent]];
if([type isKindOfSpecification:myObjectFileType]) {
XCDependencyNode* inputNode = [context dependencyNodeForName:input createIfNeeded:YES];
[outputNode addDependedNode:inputNode];
[dep addArgument:input];
} else {
[context addDependencyAnalysisWarningMessageFormat:
@"warning: skipping file '%@' (unexpected file type '%@' in link phase)",
input, [type identifier]];
}
}
return [NSArray arrayWithObject:outputNode];
}
@end</pre>
<!-- --- --- --- --- -->
<h2><a name="outputparsing">5. Parsing the ouput of command line tools</a></h2>
<p>TODO</p>
<!-- --- --- --- --- -->
<h2><a name="producttype">6. Product Type Sample Code (<code>.pbprodspec</code>)</a></h2>
<p>Specification file <code>myproducttype.pbprodspec</code> :</p>
<pre>{
Identifier = com.domain.me.product-type.tool;
Class = MyProductTypeSpecification;
Name = "My Command-line Tool";
Description = "My Command-line Tool";
//Image name for inactive target is &lt;IconNamePrefix&gt;
//Image name for active target is &lt;IconNamePrefix&gt;Active
IconNamePrefix = "TargetPlugin";
DefaultTargetName = "MyTool";
DefaultBuildProperties = { &hellip; };
AllowedBuildPhaseTypes = ( Headers, Sources, Frameworks );
BuildPropertiesToAppend = ??;
PackageTypes = (
com.domain.me.package-type.tool
);
}</pre>
<p>Code file <code>MyProductTypeSpecification.m</code> :</p>
<pre>#import "MyProductTypeSpecification.h"
#import "XCPBuildSystem.h"
@implementation MyProductTypeSpecification
- (void)computeProductDependenciesInTargetBuildContext:(PBXTargetBuildContext*)context
{
NSString* productPath = [context expandedValueForString:@"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)"];
XCDependencyNode* productNode = [context dependencyNodeForName:productPath createIfNeeded:YES];
[context addProductNode:productNode];
}
- (XCLinkerSpecification*)linkerSpecificationForObjectFilesInTargetBuildContext:(PBXTargetBuildContext*)context
{
return [[XCLinkerSpecification specificationRegistry] objectForKey:@"com.domain.me.linker"];
}
@end</pre>
<!-- --- --- --- --- -->
<p class="footer">
Copyright © 2005-2006 Damien Bobillot,
E-Mail : <a href="mailto:damien.bobillot.2002_xcodeplugin CHEZ m4x.org?Subject=%5BXcode Plugins%5D%20">damien.bobillot.2002_xcodeplugin CHEZ m4x.org</a>
</p>
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-445657-1";
urchinTracker();
</script>
</body>
</html>
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.