Skip to content

Commit dd329d7

Browse files
feat: link native modules to custom cocopod (#9)
* Fix colo loco linking * Add podspec * Linker file for pods * Clean up pbxproj * Update pod file * Explain flags in podspec * WIP -- probably abandon, for Sean's work -- but maybe use the color updates * Remove comments and just link docs for brevity * Include any native files * Remove cleanup file * Rename files and functions * Ad color changes to new cocopod script --------- Co-authored-by: Jamon Holmgren <code@jamon.dev>
1 parent 267b25f commit dd329d7

File tree

9 files changed

+429
-174
lines changed

9 files changed

+429
-174
lines changed

IRNativeModules.podspec

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Pod::Spec.new do |s|
2+
s.name = "IRNativeModules"
3+
s.version = "1.0.0"
4+
s.summary = "Colocated native modules for Reactotron"
5+
s.description = "A unified podspec for all colocated native modules in the Reactotron app"
6+
s.homepage = "https://github.com/infinitered/reactotron-macos"
7+
s.license = "MIT"
8+
s.authors = { "Infinite Red" => "hello@infinite.red" }
9+
s.source = { :path => "." }
10+
s.platform = :osx, "11.0"
11+
s.requires_arc = true
12+
13+
# Include all native modules and generated files
14+
s.source_files = [
15+
"app/**/*.{h,m,mm,c,cpp,swift}",
16+
"app/utils/experimental/*.{h,m,mm,swift}",
17+
"macos/build/generated/colocated/**/*.{h,m,mm,c,cpp,swift}"
18+
]
19+
20+
# Exclude problematic files if needed
21+
# s.exclude_files = []
22+
23+
s.dependency 'React-Core'
24+
s.dependency 'ReactCodegen'
25+
s.dependency 'React-Fabric'
26+
s.dependency 'RCT-Folly'
27+
s.dependency 'Yoga'
28+
29+
# Compiler configuration for React Native Fabric components. Folly documentation: https://github.com/facebook/folly
30+
s.compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32 -DFOLLY_CFG_NO_COROUTINES'
31+
32+
s.pod_target_xcconfig = {
33+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
34+
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/RCT-Folly" "$(PODS_ROOT)/DoubleConversion" "$(PODS_CONFIGURATION_BUILD_DIR)/ReactCodegen" "$(PODS_ROOT)/React-Fabric" "$(PODS_ROOT)/Headers/Private/React-Fabric" "$(PODS_ROOT)/Headers/Private/Yoga"'
35+
}
36+
end

bin/generateTurboModule.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,36 @@
33
const fs = require("fs")
44
const path = require("path")
55

6+
// Check if NO_COLOR is set
7+
const nc = (t) => (process.env.NO_COLOR ? "" : t)
8+
69
// Color constants for console output
7-
const red = "\x1b[31m"
8-
const green = "\x1b[32m"
9-
const yellow = "\x1b[33m"
10-
const blue = "\x1b[34m"
11-
const dkgray = "\x1b[90m"
10+
const red = nc("\x1b[31m")
11+
const green = nc("\x1b[32m")
12+
const yellow = nc("\x1b[33m")
13+
const blue = nc("\x1b[34m")
14+
const dkgray = nc("\x1b[90m")
1215
const x = "\x1b[0m"
1316

1417
// Print colored output
1518
function printSuccess(message) {
16-
console.log(` ${green}${x}${dkgray} ${message}`)
19+
console.log(` ${green}${x}${dkgray} ${message}${x}`)
1720
}
1821

1922
function printError(message) {
20-
console.log(` ${red}${x}${dkgray} ${message}`)
23+
console.log(` ${red}${x}${dkgray} ${message}${x}`)
2124
}
2225

2326
function printWarning(message) {
24-
console.log(` ${yellow}${x}${dkgray} ${message}`)
27+
console.log(` ${yellow}${x}${dkgray} ${message}${x}`)
2528
}
2629

2730
function printInfo(message) {
28-
console.log(` ${blue}${x}${dkgray} ${message}`)
31+
console.log(` ${blue}${x}${dkgray} ${message}${x}`)
2932
}
3033

3134
function printStep(message) {
32-
console.log(` ${dkgray}${x}${dkgray} ${message}`)
35+
console.log(` ${dkgray}${x}${dkgray} ${message}${x}`)
3336
}
3437

3538
// Parse turbomodule comment and extract module info

bin/generate_native_files.rb

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# CocoaPods-based React Native Colo Loco
2+
# This version generates files for the IRNativeModules pod instead of directly linking to Xcode
3+
require 'fileutils'
4+
require 'pathname'
5+
6+
# Color constants for output
7+
if ENV['NO_COLOR']
8+
R = "" # Red
9+
RB = "" # Red bold
10+
G = "" # Green
11+
GB = "" # Green bold
12+
BB = "" # Blue bold
13+
Y = "" # Yellow
14+
YB = "" # Yellow bold
15+
D = "" # Dark gray
16+
DD = "" # Darker gray
17+
DB = "" # Dark gray bold
18+
DDB = "" # Darker gray bold
19+
S = "" # Strikethrough
20+
X = "\033[0m" # Reset
21+
elsif ENV['PREFERS_CONTRAST'] == 'more'
22+
R = "\033[91m" # Bright red
23+
RB = "\033[91m" # Bright red
24+
G = "\033[92m" # Bright green
25+
GB = "\033[92m" # Bright green
26+
BB = "\033[94m" # Bright blue
27+
Y = "\033[93m" # Bright yellow
28+
YB = "\033[93m" # Bright yellow
29+
D = "\033[37m" # White
30+
DD = "\033[37m" # White
31+
DB = "\033[37m" # White
32+
DDB = "\033[37m" # White
33+
S = "\033[9m" # Strikethrough
34+
X = "\033[0m" # Reset
35+
else
36+
R = "\033[31m" # Red
37+
RB = "\033[31;1m" # Red bold
38+
G = "\033[32m" # Green
39+
GB = "\033[32;1m" # Green bold
40+
BB = "\033[32;1m" # Blue bold
41+
Y = "\033[33m" # Yellow
42+
YB = "\033[33;1m" # Yellow bold
43+
D = "\033[90m" # Dark gray
44+
DD = "\033[90;2m" # Darker gray
45+
DB = "\033[90;1m" # Dark gray bold
46+
DDB = "\033[90;1;2m" # Darker gray bold
47+
S = "\033[9m" # Strikethrough
48+
X = "\033[0m" # Reset
49+
end
50+
51+
$changes_made = false
52+
53+
puts "#{X}"
54+
55+
# This method will generate files for the IRNativeModules pod
56+
# instead of directly modifying the Xcode project
57+
def generate_native_files_for_pod(options = {})
58+
puts "🤪 #{BB}React Native Colo Loco (CocoaPods)#{X}"
59+
puts ""
60+
puts "#{D}Running ./bin/generate_native_files.rb from #{__FILE__}#{X}"
61+
puts ""
62+
63+
_verify_options!(options)
64+
65+
app_path = options[:app_path]
66+
project_root = options[:project_root] || Pathname.pwd
67+
clean = options[:clean] || false
68+
69+
relative_app_path = Pathname.new(app_path).relative_path_from(project_root)
70+
71+
puts "#{D} App Path: #{BB}#{relative_app_path}#{X}"
72+
puts "#{D} Project Root: #{G}#{project_root}#{X}"
73+
puts "#{D} clean: #{G}#{clean}#{X}" if clean
74+
puts ""
75+
76+
return unless _check_app_path(app_path)
77+
78+
generated_files_path = File.join(project_root, 'macos', 'build', 'generated', 'colocated')
79+
80+
# if clean is true, remove the generated files
81+
if clean
82+
_clean_generated_files(generated_files_path, project_root)
83+
return
84+
end
85+
86+
# Ensure the generated files directory exists
87+
FileUtils.mkdir_p(generated_files_path)
88+
89+
# Run the ./generateTurboModule.js script to generate any embedded TurboModules
90+
_generate_turbomodules(project_root)
91+
92+
# Get all colocated files in the app_path
93+
colocated_files = Dir.glob(File.join(app_path, '**/*.{h,m,mm,c,swift,cpp}')).map { |file| Pathname.new(file).realpath }
94+
95+
puts "#{D}Processing #{colocated_files.length} native files for CocoaPods#{X}"
96+
puts ""
97+
98+
# Generate headers for .mm files that don't have them
99+
colocated_files.each do |file|
100+
if file.extname == '.m' || file.extname == '.mm'
101+
# Check if there's already a header file
102+
header_file = file.sub_ext('.h')
103+
next if colocated_files.any? { |f| f.basename == header_file.basename }
104+
next if file.basename.to_s.include?('ComponentView') # Skip component views
105+
106+
_generate_objc_header(file, generated_files_path, project_root)
107+
end
108+
end
109+
110+
# Also generate headers for any .mm files in the generated directory
111+
generated_mm_files = Dir.glob(File.join(generated_files_path, '*.mm')).map { |file| Pathname.new(file).realpath }
112+
generated_mm_files.each do |file|
113+
# Check if header already exists
114+
header_file = file.sub_ext('.h')
115+
next if File.exist?(header_file)
116+
117+
_generate_objc_header(file, generated_files_path, project_root)
118+
end
119+
120+
if $changes_made
121+
puts ""
122+
puts "#{GB}✓ Files generated for IRNativeModules pod#{X}"
123+
puts "#{D} Run 'pod install' to link the updated files to Xcode#{X}"
124+
puts ""
125+
else
126+
puts ""
127+
puts "#{D}No changes needed.#{X}"
128+
puts ""
129+
end
130+
end
131+
132+
def _verify_options!(options)
133+
if options[:app_path].nil?
134+
raise "#{RB}link_colocated_native_files_for_pod - You must specify an app_path#{X}"
135+
end
136+
end
137+
138+
def _check_app_path(app_path)
139+
return true if File.exist?(app_path)
140+
puts "#{RB}React Native Colo Loco error:#{X}"
141+
puts ""
142+
puts "#{Y} No files found in #{BB}#{app_path}#{X}#{Y}.#{X}"
143+
puts "#{Y} Please check your app_path.#{X}"
144+
puts ""
145+
return false
146+
end
147+
148+
def _clean_generated_files(generated_files_path, project_root)
149+
$changes_made = true
150+
relative_path = Pathname.new(generated_files_path).relative_path_from(project_root)
151+
152+
if File.exist?(generated_files_path)
153+
FileUtils.rm_rf(generated_files_path)
154+
puts "#{YB} ➖ Removed #{X}#{S}#{D}#{relative_path}#{X}#{D} folder#{X}"
155+
end
156+
157+
puts ""
158+
puts "#{GB}✓ Generated files cleaned#{X}"
159+
puts ""
160+
end
161+
162+
def _generate_objc_header(objc_file, generated_files_path, project_root)
163+
return if objc_file.extname != '.m' && objc_file.extname != '.mm'
164+
165+
template_header = File.read(File.join(File.dirname(__FILE__), 'templates', 'MyTemplate.h'))
166+
167+
file_no_ext = objc_file.basename.sub_ext('')
168+
header_file_path = File.join(generated_files_path, "#{file_no_ext}.h")
169+
170+
# Don't regenerate if file exists and is newer than source
171+
if File.exist?(header_file_path) && File.mtime(header_file_path) > File.mtime(objc_file)
172+
relative_header = Pathname.new(header_file_path).relative_path_from(project_root)
173+
puts "#{DB} ✔️ Exists #{X}#{DB}#{relative_header.basename}#{X} #{DD}#{relative_header.dirname}#{X}"
174+
return
175+
end
176+
177+
header_file_content = template_header.gsub("MyTemplate", file_no_ext.to_s)
178+
FileUtils.mkdir_p(File.dirname(header_file_path))
179+
File.write(header_file_path, header_file_content)
180+
181+
relative_header = Pathname.new(header_file_path).relative_path_from(project_root)
182+
puts "#{BB} ➕ Generated #{X} #{BB}#{relative_header.basename}#{X} #{DD}#{relative_header.dirname}#{X}"
183+
$changes_made = true
184+
185+
return Pathname.new(header_file_path)
186+
end
187+
188+
def _generate_turbomodules(project_root)
189+
puts "#{D}Generating embedded TurboModules#{X}"
190+
puts ""
191+
result = `node #{project_root}/bin/generateTurboModule.js generate 2>&1`
192+
puts result
193+
puts ""
194+
$changes_made = true if result.include?("Generated") || result.include?("processed")
195+
end
196+
197+
def _info_file_exists(relative_path)
198+
return "#{DB} ✔️ Exists #{X}#{DB}#{relative_path.basename}#{X} #{DD}#{relative_path.dirname}#{X}"
199+
end
200+
201+
def _err_not_exists(file)
202+
return "#{YB} ⚠️ Warning #{X}#{D}#{file}#{X}#{D} does not exist, skipping#{X}"
203+
end

0 commit comments

Comments
 (0)