xcode_make 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/xcode-make ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #require 'xcode_make'
4
+ #XcodeMake.run_make_wrapper(*ARGV)
5
+
6
+ MAKE_BINARY = '/usr/bin/make'
7
+
8
+ project_file_path = ENV['PROJECT_FILE_PATH']
9
+ if project_file_path.nil? || project_file_path.empty?
10
+ # Seems we're not being run from within Xcode. No point in doing
11
+ # the wrapping and parsing if we have no project file to write to.
12
+ exec(MAKE_BINARY, *ARGV)
13
+ end
14
+
15
+ Signal.trap("SIGINT") do
16
+ exit(128 + 2)
17
+ end
18
+
19
+ require 'Open3'
20
+ MARKER = '!!!xmake!!!'
21
+ TMP_FILE = 'xcode-make.tmp'
22
+
23
+ # Make sure our wrappers are called, so we can print the PWD
24
+ ENV['PATH'] = "#{File.dirname(__FILE__)}/wrappers:#{ENV['PATH']}"
25
+
26
+ compile_step = Regexp.new("^#{MARKER} PWD=(\\S+?)\\s.*\\s-c(\\s|$)")
27
+ ignored_compiler_parts = Regexp.new('(^| )*-(o|x|include|arch|Xclang|name) +[^ ]+ *')
28
+ source_file = Regexp.new('(^| )([^- ][^ ]*\.(c|cpp|m|mm))( |$)')
29
+ defines_and_includes = Regexp.new('\s(-D\S+|-I\S+)')
30
+
31
+ make_directory_switch = Regexp.new('^make.*?: Entering directory `(.*?)\'$')
32
+ issue = Regexp.new('^(.+?):(\d+:){0,2} (warning|error|info): (.*)$')
33
+
34
+ discovered_files = {}
35
+
36
+ ENV['PROJECT_TEMP_DIR'] = "/tmp"
37
+ tmp_file_path = File.join(ENV['PROJECT_TEMP_DIR'], TMP_FILE)
38
+ if File.exists?(tmp_file_path)
39
+ File.open(tmp_file_path, "rb") {|f| discovered_files = Marshal.load(f)}
40
+ end
41
+
42
+ ARGV.unshift(MAKE_BINARY)
43
+
44
+ # Try to catch errors from makefiles by having make print the
45
+ # working directory for each recursive makefile.
46
+ ARGV.push('--print-directory')
47
+ make_directory = ''
48
+
49
+ # Sadly we can't use the same working directory for compilation issues,
50
+ # as make might have recursed to another directory by the time clang
51
+ # finishes compilation and prints issues to stderr. This should not be
52
+ # an issue with make itself, assuming it flushes stderr each time it
53
+ # recurses into a new directory.
54
+
55
+ build_exit_status = 0
56
+
57
+ Open3.popen2e(*ARGV) do |stdin, stdout_and_stderr, wait_thr|
58
+ stdout_and_stderr.each do |line|
59
+
60
+ if line.start_with?(MARKER)
61
+ compiler_line = compile_step.match(line)
62
+ next if not compiler_line
63
+
64
+ pwd = compiler_line[1]
65
+
66
+ file_name = line.gsub(ignored_compiler_parts, ' ')
67
+ file_name = source_file.match(file_name)
68
+ next if not file_name
69
+
70
+ # The input filename should already be absolute, thanks to
71
+ # the work done in the wrapper.
72
+ file_name = file_name[0].strip
73
+
74
+ compiler_flags = ""
75
+ line.scan(defines_and_includes) do |define_or_include|
76
+ define_or_include = define_or_include[0]
77
+ define_or_include.match(/-I(.+)/) do |include|
78
+ # We still need to make sure include paths are absolute, as the
79
+ # wrapper does not handle that part.
80
+ define_or_include = "-I#{File.absolute_path(include[1], pwd)}"
81
+ end
82
+
83
+ compiler_flags += " #{define_or_include}"
84
+ end
85
+
86
+ discovered_files[file_name] = compiler_flags.strip
87
+
88
+ next # Early return, we don't want to print these lines
89
+
90
+ elsif make_directory_switch.match(line)
91
+ make_directory = $1
92
+
93
+ elsif issue.match(line)
94
+ issue_file = $1
95
+
96
+ # This will only have an effect on issues reported by make, as clang
97
+ # will report absolute file names thanks to the work done in the wrapper.
98
+ line.gsub!(issue_file, File.absolute_path(issue_file, make_directory))
99
+ end
100
+
101
+ # Make sure the output reaches Xcode
102
+ puts line
103
+ $stdout.flush
104
+ end
105
+
106
+ build_exit_status = wait_thr.value.exitstatus
107
+ end
108
+
109
+ if build_exit_status > 0
110
+ # Save any discovered files for the next possibly successful build
111
+ File.open(tmp_file_path, "wb") do |file|
112
+ Marshal.dump(discovered_files, file)
113
+ end
114
+
115
+ exit(build_exit_status)
116
+ end
117
+
118
+ # We're now ready to update the Xcode project file
119
+
120
+ puts "xcode-make: Build completed successfully. Processing discovered files..."
121
+ $stdout.flush
122
+
123
+ old_verbose = $VERBOSE
124
+ $VERBOSE = nil # Ignore warnings from xcoder
125
+ require 'xcoder'
126
+ $VERBOSE = old_verbose
127
+
128
+ # FIXME: Read encoding from project file instead of assuming UTF-8
129
+ Encoding.default_external = 'utf-8'
130
+ project = Xcode.project(project_file_path)
131
+
132
+ indexer_target = project.targets.find{|t| t.name == 'Indexer' }
133
+ if not indexer_target
134
+ # Create the indexer target
135
+ indexer_target = project.create_target('Indexer', :native)
136
+ project.global_configs.each do |config|
137
+ indexer_target.create_configuration(config.name) do |new_config|
138
+ # For some reason we need PRODUCT_NAME. Inherit the rest from the global config
139
+ new_config.build_settings = { 'PRODUCT_NAME' => '$(TARGET_NAME)' }
140
+ end
141
+ end
142
+
143
+ # TODO: Remove added scheme for Indexer (requires support from xcoder)
144
+ # TODO: Add Indexer as Analyze step to the main build target's scheme
145
+
146
+ project.save!
147
+ end
148
+
149
+ sources_build_phase = indexer_target.sources_build_phase || indexer_target.create_build_phase(:sources)
150
+
151
+ existing_sources = Hash[ *sources_build_phase.files.collect { |file| [ file.file_ref.path, file.settings['COMPILER_FLAGS'] ] }.flatten ]
152
+
153
+ changed_sources = discovered_files.dup.delete_if { |k, v| existing_sources[k] == v }
154
+
155
+ puts "xcode-make: Existing: #{existing_sources.length}, discovered: #{discovered_files.length}, changed: #{changed_sources.length}"
156
+
157
+ changed_sources.each do |file_name, compiler_flags|
158
+
159
+ file = sources_build_phase.file(file_name)
160
+ if not file
161
+ basename = File.basename(file_name)
162
+ file_reference = Xcode::FileReference.file({ "path" => file_name, 'name' => basename, 'sourceTree' => '<absolute>'})
163
+ file_reference = project.registry.add_object(file_reference)
164
+
165
+ sources_build_phase.add_build_file(file_reference, { 'COMPILER_FLAGS' => compiler_flags })
166
+ puts "xcode-make: Added #{file_name} to indexer"
167
+
168
+ else
169
+ file.settings['COMPILER_FLAGS'] = compiler_flags
170
+ file.save!
171
+ puts "xcode-make: Updated compiler flags for #{file_name}"
172
+ end
173
+
174
+ $stdout.flush
175
+ end
176
+
177
+ if changed_sources.length > 0
178
+ puts "xcode-make: Saving project file..."
179
+ # Saving the project is not atomic, as xcoder does a second step of reformatting,
180
+ # after the initial save. We do a manual save to make it appear atomic for Xcode.
181
+ tmp_project_path = '/tmp/xmake'
182
+ project.save(tmp_project_path)
183
+ FileUtils.copy(File.join(tmp_project_path, 'project.pbxproj'), project_file_path)
184
+ end
185
+
186
+ # No need for the temp file anymore
187
+ File.delete(tmp_file_path) if File.exists?(tmp_file_path)
188
+
189
+ puts "xcode-make: Done!"
190
+
191
+ exit(build_exit_status)
@@ -0,0 +1,15 @@
1
+ # Trick mkmf into linking a binary instead of a shared library
2
+ require 'rbconfig'
3
+ RbConfig::MAKEFILE_CONFIG['LINK_SO'] = "$(CXX) -o $@ $(OBJS) $(LDFLAGS)"
4
+
5
+ require 'mkmf'
6
+ CONFIG['DLEXT'] = "bin"
7
+ create_makefile('../bin/wrappers/wrapper') do |config|
8
+ config = configuration('') << <<RULES
9
+ all:
10
+ install: symlink
11
+ symlink: install-so
12
+ ln -s $(RUBYARCHDIR)/$(DLLIB) $(RUBYARCHDIR)/clang
13
+ ln -s $(RUBYARCHDIR)/$(DLLIB) $(RUBYARCHDIR)/clang++
14
+ RULES
15
+ end
@@ -0,0 +1,81 @@
1
+ #include <dirent.h>
2
+ #include <libgen.h>
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <string>
7
+ #include <unistd.h>
8
+
9
+ #include <mach-o/dyld.h>
10
+
11
+ using namespace std;
12
+
13
+ void replace_all(std::string& str, const std::string& from, const std::string& to)
14
+ {
15
+ if (from.empty())
16
+ return;
17
+
18
+ size_t start_pos = 0;
19
+ while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
20
+ str.replace(start_pos, from.length(), to);
21
+ start_pos += to.length();
22
+ }
23
+ }
24
+
25
+ int main(int argc, char** argv)
26
+ {
27
+ char* program_copy = strdup(argv[0]);
28
+ char* program_name = basename(program_copy);
29
+ free(program_copy);
30
+
31
+ if (strcmp(program_name, "wrapper.bin") == 0) {
32
+ printf("usage: symlink %s to an application of your choice\n", program_name);
33
+ return 64;
34
+ }
35
+
36
+ char binary_path[2048];
37
+ uint32_t size = sizeof(binary_path);
38
+ if (_NSGetExecutablePath(binary_path, &size) != 0) {
39
+ fprintf(stderr, "failed to get executable path!");
40
+ return 1;
41
+ }
42
+
43
+ char* binary_dirname = dirname(binary_path);
44
+
45
+ char* absolute_path = realpath(binary_dirname, NULL);
46
+ string absolute_path_string(absolute_path);
47
+ free(absolute_path);
48
+
49
+ string path(getenv("PATH"));
50
+ absolute_path_string.append(":");
51
+ replace_all(path, absolute_path_string, "");
52
+ setenv("PATH", path.c_str(), 1);
53
+
54
+ char* working_path = realpath(".", NULL);
55
+ printf("!!!xmake!!! PWD=%s %s", working_path, program_name);
56
+ free(working_path);
57
+
58
+ for (int i = 1; i < argc; i++) {
59
+ // FIXME: We might need better detection of what we concider the
60
+ // input file than just looking at the extension.
61
+ if (strcmp(argv[i] + strlen(argv[i]) - 4, ".cpp") == 0
62
+ || strcmp(argv[i] + strlen(argv[i]) - 2, ".c") == 0
63
+ || strcmp(argv[i] + strlen(argv[i]) - 3, ".mm") == 0
64
+ || strcmp(argv[i] + strlen(argv[i]) - 2, ".m") == 0) {
65
+
66
+ // By making the input filename absolute, we trick clang into
67
+ // outputting errors and warnings with an absolute path as well,
68
+ // which lets Xcode correctly pinpoint the file in question.
69
+ argv[i] = realpath(argv[i], NULL);
70
+ }
71
+
72
+ printf(" %s", argv[i]);
73
+ }
74
+ printf("\n");
75
+
76
+ fflush(stdout);
77
+ execvp(program_name, argv);
78
+
79
+ fprintf(stderr, "xmake: command not found: %s\n", program_name);
80
+ return 127;
81
+ }
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xcode_make
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tor Arne Vestbø
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: xcoder
16
+ requirement: &70203812621260 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.12
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70203812621260
25
+ description: A wrapper for using Xcode's indexer with external targets
26
+ email: torarnv@gmail.com
27
+ executables:
28
+ - xcode-make
29
+ extensions:
30
+ - ext/xcode_make/extconf.rb
31
+ extra_rdoc_files: []
32
+ files:
33
+ - ext/xcode_make/wrapper.cpp
34
+ - ext/xcode_make/extconf.rb
35
+ - bin/xcode-make
36
+ homepage: https://github.com/torarnv/xcode-make
37
+ licenses: []
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.9.2
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.10
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Xcode-make!
60
+ test_files: []