@@ -31,25 +31,56 @@ var force = false, debug = false;
3131var
3232 arch = process . arch ,
3333 platform = process . platform ,
34- v8 = / [ 0 - 9 ] + \. [ 0 - 9 ] + / . exec ( process . versions . v8 ) [ 0 ] ;
34+ v8 = / [ 0 - 9 ] + \. [ 0 - 9 ] + / . exec ( process . versions . v8 ) [ 0 ] ,
35+ environment = { ...process . env } ;
36+
37+ var patchLibPath = false , patchTool = null ;
38+
3539var args = process . argv . slice ( 2 ) . filter ( function ( arg ) {
3640 if ( arg === '-f' ) {
3741 force = true ;
3842 return false ;
3943 } else if ( arg . substring ( 0 , 13 ) === '--target_arch' ) {
4044 arch = arg . substring ( 14 ) ;
45+ force = true ; // No point trying to run a binary that isn't the host's arch
46+ } else if ( arg . substring ( 0 , 13 ) === '--target_plat' ) {
47+ platform = arg . substring ( 14 ) ;
48+ force = true ; // No point trying to run a binary that isn't the host's platform
49+ environment = patchEnvironment ( environment ) ;
50+ return false ;
4151 } else if ( arg === '--debug' ) {
4252 debug = true ;
43- }
53+ } else if ( arg . substring ( 0 , 12 ) === '--patch_path' ) {
54+ patchLibPath = arg . substring ( 13 ) ;
55+
56+ if ( ! environment . hasOwnProperty ( "PATCH_TOOL" ) ) {
57+ console . error ( "--patch_path was requested but PATCH_TOOL env was not set; aborting" ) ;
58+ console . error ( "be sure to set --target_plat first if cross-compiling as it will set PATCH_TOOL for you" ) ;
59+ process . exit ( 1 ) ;
60+ }
61+
62+ return false ;
63+ }
64+
4465 return true ;
4566} ) ;
67+
68+ // If we are cross-compiling for darwin, we must instruct gyp to generate a makefile which is
69+ // compatible with the MacOS tooling. YOU MUST use the -f variant (NOT --format=) because it
70+ // overrides -f set by node-gyp, and node-gyp isn't clever enough to know that they're aliases.
71+ var configure_args = ( platform != process . platform && platform == "darwin" )
72+ ? [ "-f" , "make-mac" ]
73+ : [ ] ;
74+
4675if ( ! { ia32 : true , x64 : true , arm : true } . hasOwnProperty ( arch ) ) {
4776 console . error ( 'Unsupported (?) architecture: `' + arch + '`' ) ;
4877 process . exit ( 1 ) ;
4978}
5079
5180// Test for pre-built library
5281var modPath = platform + '-' + arch + '-v8-' + v8 ;
82+ var command = process . platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp' ;
83+
5384if ( ! force ) {
5485 try {
5586 fs . statSync ( path . join ( __dirname , 'bin' , modPath , 'capnp.node' ) ) ;
@@ -70,36 +101,46 @@ if (!force) {
70101 build ( ) ;
71102}
72103
73- // Build it
74- function build ( ) {
104+ function nodeGyp ( task , arguments = [ ] , gyp_args = [ ] ) {
105+ var final_args = [ task ] . concat ( arguments , "--" , gyp_args ) ;
106+ console . log ( `Executing ${ command } with the following args: ` ) ;
107+ console . log ( final_args ) ;
108+
75109 var sp = cp . spawn (
76- process . platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp' ,
77- [ 'rebuild' ] . concat ( args ) ,
78- { customFds : [ 0 , 1 , 2 ] } ) ;
110+ command ,
111+ final_args ,
112+ {
113+ stdio : 'inherit' ,
114+ env : environment ,
115+ } ) ;
79116
80- sp
81- . on ( 'close' , function ( ) { afterBuild ( ) ; } )
82- . on ( 'exit' , function ( err ) {
117+ sp . on ( 'exit' , function ( err ) {
83118 if ( err ) {
84119 if ( err === 127 ) {
85120 console . error (
86121 'node-gyp not found! Please upgrade your install of npm! You need at least 1.1.5 (I think) ' +
87122 'and preferably 1.1.30.'
88123 ) ;
89124 } else {
90- console . error ( ' Build failed' ) ;
125+ console . error ( ` Build step ${ task } failed with exit code ${ err } ` ) ;
91126 }
92127 return process . exit ( err ) ;
93128 }
94129 } ) ;
95130
96- if ( sp . stdout ) {
97- sp . stdout . pipe ( process . stdout ) ;
98- }
131+ return sp ;
132+ }
99133
100- if ( sp . stderr ) {
101- sp . stderr . pipe ( process . stderr ) ;
102- }
134+ // Build it
135+ function build ( ) {
136+ console . log ( "Building with the following environment:" ) ;
137+ console . log ( environment ) ;
138+
139+ nodeGyp ( "clean" ) . on ( 'close' , ( ) => {
140+ nodeGyp ( "configure" , args , configure_args ) . on ( 'close' , ( ) => {
141+ nodeGyp ( "build" , args ) . on ( 'close' , ( ) => afterBuild ( ) ) ;
142+ } ) ;
143+ } ) ;
103144}
104145
105146// Move it to expected location
@@ -120,6 +161,10 @@ function afterBuild() {
120161 process . exit ( 1 ) ;
121162 }
122163
164+ if ( patchLibPath !== false ) {
165+ patchLibs ( environment . PATCH_TOOL , patchLibPath , targetPath ) ;
166+ }
167+
123168 try {
124169 fs . renameSync ( targetPath , installPath ) ;
125170 console . log ( 'Installed in `' + installPath + '`' ) ;
@@ -128,3 +173,73 @@ function afterBuild() {
128173 }
129174}
130175
176+ /**
177+ * Rewrite the dynamic library references in the produced bundle to have appropriate paths for
178+ * relative layout within the final application bundle.
179+ *
180+ * @param patchTool - command name to use for editing the binary
181+ * @param patchPath - the path to be stripped out of the binary
182+ * @param target - The target binary to be modified
183+ */
184+ function patchLibs ( patchTool , patchPath , target ) {
185+ var libVersion = "0.8.0" ;
186+ var libExt = "so" ;
187+ var libs = [
188+ "libkj" ,
189+ "libkj-async" ,
190+ "libcapnp" ,
191+ "libcapnpc" ,
192+ "libcapnp-rpc" ,
193+ ] ;
194+
195+ var patchArgs = libs . flatMap ( ( lib ) => {
196+ return [ "-change" , `${ patchPath } /${ lib } -${ libVersion } .${ libExt } ` , `@executable_path/${ lib } -${ libVersion } .dylib` ] ;
197+ } ) . concat ( [ target ] ) ;
198+
199+ console . log ( `Invoking binary patch tool ${ patchTool } with the following arguments:` ) ;
200+ console . log ( patchArgs ) ;
201+
202+ /*
203+ * In future if this errors, we may need to set headerpad_max_install_names in LDFLAGS too...
204+ * (this provides more space for the dylib names in the object so that they can be rewritten
205+ * post-link) - looks like we don't need to do this for now though?
206+ */
207+ var patchProcess = cp . spawnSync ( patchTool , patchArgs , {
208+ stdio : 'inherit' ,
209+ } ) ;
210+
211+ if ( patchProcess . error ) {
212+ console . error ( `Failed to patch binary: ${ patchProcess . error } ` ) ;
213+ process . exit ( 1 ) ;
214+ } else if ( patchProcess . status !== 0 ) {
215+ console . error ( `Failed to patch binary: ${ patchTool } exited with code ${ patchProcess . status } ` ) ;
216+ process . exit ( 1 ) ;
217+ } else {
218+ console . log ( "Binary patched successfully." ) ;
219+ }
220+ }
221+
222+ /**
223+ * Modify the provided environment object to be appropriate for cross-compilation for a darwin
224+ * target from a linux host
225+ */
226+ function patchEnvironment ( env ) {
227+ if ( ! env . hasOwnProperty ( "CAPNP_LIBDIR" ) ) {
228+ console . error ( "CAPNP_LIBDIR must be set to a place I can find compiled libcapnp, libkj and friends for the target plat/arch" ) ;
229+ process . exit ( 1 ) ;
230+ }
231+
232+ if ( ! env . hasOwnProperty ( "CAPNP_INCDIR" ) ) {
233+ console . error ( "CAPNP_INCDIR must be set to a place I can find headers for libcapnp and libkj!" ) ;
234+ process . exit ( 1 ) ;
235+ }
236+
237+ return {
238+ CC : "o64-clang" ,
239+ CXX : "o64-clang++" ,
240+ CXXFLAGS : "-mmacosx-version-min=10.7 -std=c++14 -stdlib=libc++ -I" + env . CAPNP_INCDIR ,
241+ LDFLAGS : "-L" + env . CAPNP_LIBDIR ,
242+ PATCH_TOOL : "x86_64-apple-darwin20.2-install_name_tool" ,
243+ ...env
244+ } ;
245+ }
0 commit comments