-
Notifications
You must be signed in to change notification settings - Fork 121
There should be way to recompile files without clang proxying (Xcode) #24
Comments
this will fix the #8 issue, and will make dyci clean and clear |
Hi, @PaulTaykalo. I've reverse-engineered Xcode and found a way to get a compiler/linker arguments for any source file in a project from inside your plugin. Compiler/linker arguments for a fileFirstly, you need to create a PBXReference for the file: Class PBXReference = NSClassFromString(@"PBXReference");
id file_ref = [[PBXReference alloc] initWithPath: @"/full/path/to/source_file.m"]; Next, search for a targets contain this file (there may be more than one target so you should perform additional checks): Class PBXProject = NSClassFromString(@"PBXProject");
id suggested_targets = [PBXProject performSelector: NSSelectorFromString(@"targetsInAllProjectsForFileReference:justNative:")
withObject: file_ref
withObject: nil];
// just a first one
id target = suggested_targets[0]; Once you have a target you can ask it for a «build context» — the thing contains all information required for building the target. id build_context = [target performSelector: NSSelectorFromString(@"targetBuildContext")]; Each «build context» has a set of commands which must be executed upon building. In terms of Xcode they're called «dependency commands». NSArray *commands = [build_context performSelector: NSSelectorFromString(@"commands")];
[commands enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSArray *rule_info = [obj performSelector: NSSelectorFromString(@"ruleInfo")];
// Dump compiler arguments for the file
if ([[rule_info firstObject] isEqualToString: @"CompileC"]) {
/**
* «Dependency commands» can contain build rules for multiple files, so
* make sure you got the right one.
*/
NSString *sourcefile_path = [rule_info objectAtIndex: 2];
if ( ! [sourcefile_path isEqualToString: @"/full/path/to/source_file.m"]) {
return;
}
NSString *compiler_path = [obj valueForKey: @"_commandPath"];
NSArray *compiler_args = [obj valueForKey: @"_arguments"];
NSLog(@"\nCompile file '%@' with command line:\n%@ %@",
[sourcefile_path lastPathComponent], [compiler_path lastPathComponent],
[compiler_args componentsJoinedByString:@" "]);
}
// Linker arguments
if ([[rule_info firstObject] isEqualToString: @"Ld"]) {
/**
* Xcode doesn't pass object files (.o) directly to the linker (it uses
* a dependency info file instead) so the only thing we can do here is to grab
* the linker arguments.
*/
NSString *linker_path = [obj valueForKey: @"_commandPath"];
NSArray *linker_arguments = [obj valueForKey: @"_arguments"];
NSLog(@"\nLinker: %@ %@", [linker_path lastPathComponent], [linker_arguments componentsJoinedByString: @" "]);
}
}]; That's all 🍰 And what if I want to get the compiler/linker arguments for every file in my project?That's simple: the only difference between a solution for a one file is that you should iterate every single target (of every single project) you have: id open_projects = [PBXProject performSelector: NSSelectorFromString: @"openProjects"];
[open_projects enumerateObjectsUsingBlock:^(id project, NSUInteger idx, BOOL *stop)
{
id all_targets = [project valueForKey: @"_targets"];
[all_targets enumerateObjectsUsingBlock:^(id target, NSUInteger idx, BOOL *stop)
{
id build_context = [target performSelector: NSSelectorFromString(@"targetBuildContext")];
// …
}
}
So here we are (^^,) Complete source code#include <objc/runtime.h>
Class PBXReference = NSClassFromString(@"PBXReference");
id file_ref = [[PBXReference alloc] initWithPath: @"/full/path/to/source_file.m"];
Class PBXProject = NSClassFromString(@"PBXProject");
id suggested_targets = [PBXProject performSelector: NSSelectorFromString(@"targetsInAllProjectsForFileReference:justNative:")
withObject: file_ref
withObject: nil];
// just a first one
id target = suggested_targets[0];
id build_context = [target performSelector: NSSelectorFromString(@"targetBuildContext")];
NSArray *commands = [build_context performSelector: NSSelectorFromString(@"commands")];
[commands enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSArray *rule_info = [obj performSelector: NSSelectorFromString(@"ruleInfo")];
// Dump compiler arguments for the file
if ([[rule_info firstObject] isEqualToString: @"CompileC"]) {
/**
* Commands can contain build rules for multiple file so
* make sure you got the right one.
*/
NSString *sourcefile_path = [rule_info objectAtIndex: 2];
if ( ! [sourcefile_path isEqualToString: @"/full/path/to/source_file.m"]) {
return;
}
NSString *compiler_path = [obj valueForKey: @"_commandPath"];
NSArray *compiler_args = [obj valueForKey: @"_arguments"];
NSLog(@"\nCompile file '%@' with command line:\n%@ %@",
[sourcefile_path lastPathComponent], [compiler_path lastPathComponent],
[compiler_args componentsJoinedByString:@" "]);
}
// Linker arguments
if ([[rule_info firstObject] isEqualToString: @"Ld"]) {
/*
* Xcode doesn't pass object files (.o) directly to the linker (it uses
* a dependency info file instead) so the only thing we can do here is to grab
* the linker arguments.
*/
NSString *linker_path = [obj valueForKey: @"_commandPath"];
NSArray *linker_arguments = [obj valueForKey: @"_arguments"];
NSLog(@"\nLinker: %@ %@", [linker_path lastPathComponent], [linker_arguments componentsJoinedByString: @" "]);
}
}]; |
Hi there. Will defenitely try it, once I get back to the Ukraine. Thank you :) |
Hi there. Playing now in different branch. It seems that I finally can get correct target + compilation flags for selected file. Now it's left to start compilation and Linking.. Default linker params will not work - because we're not creating app binary again. We're creating dlyb which will contain only one file.
|
And there's still appCode... |
And here's the news. Will try to check if there's something I can do to make it work in AppCode |
Oh, that's great news, Paul 👍 About AppCode: can't we contact AppCode team directly? They don't seem to be as unreachable as Xcode guys, so let's take a chance (^^,) |
Update here :) |
This is done for Xcode in #87 |
There SHOULD be way.
Any suggestions are appreciated.
This should work for Xcode and AppCode For Any project.
The text was updated successfully, but these errors were encountered: