2 # Copyright (C) 2009, Parrot Foundation.
7 pbc_to_exe - compile bytecode to executable
14 pbc_to_exe my.pbc --install
17 Warning! With --install there must be no directory prefix in the first arg yet.
24 load_bytecode 'config.pbc'
31 (infile, cfile, objfile, exefile) = 'handle_args'(argv)
32 unless infile > '' goto err_infile
35 .local string code_type
36 code_type = 'determine_code_type'()
38 .local string codestring
39 if code_type == 'gcc' goto code_for_gcc
40 if code_type == 'msvc' goto code_for_msvc
43 codestring = 'generate_code_gcc'(infile)
46 codestring = 'generate_code_msvc'(infile)
49 codestring = 'generate_code'(infile)
55 outfh = open cfile, 'w'
56 unless outfh goto err_outfh
57 print outfh, <<'HEADER'
58 #include "parrot/parrot.h"
59 #include "parrot/embed.h"
60 const void * get_program_code(void);
63 print outfh, codestring
66 int main(int argc, char *argv[])
70 const unsigned char *program_code_addr;
72 program_code_addr = (const unsigned char *)get_program_code();
73 if (!program_code_addr)
76 Parrot_set_config_hash();
78 interp = Parrot_new( NULL );
82 Parrot_init_stacktop(interp, &interp);
83 Parrot_set_executable_name(interp,
84 Parrot_str_new(interp, argv[0], 0));
85 Parrot_set_flag(interp, PARROT_DESTROY_FLAG);
87 pf = PackFile_new(interp, 0);
91 if (!PackFile_unpack(interp, pf,
92 (const opcode_t *)program_code_addr, bytecode_size))
95 do_sub_pragmas(interp, pf->cur_cs, PBC_PBC, NULL);
97 Parrot_pbc_load(interp, pf);
99 PackFile_fixup_subs(interp, PBC_MAIN, NULL);
100 Parrot_runcode(interp, argc, argv);
101 Parrot_destroy(interp);
102 Parrot_exit(interp, 0);
107 # The close opcode does not return a result code,
108 # use the method instead.
109 .local int closeresult
110 closeresult = outfh.'close'()
111 unless closeresult == 0 goto err_close
114 .local string extra_obj
116 if code_type != 'msvc' goto no_extra
117 extra_obj = 'replace_pbc_extension'(infile, '.RES')
121 'compile_file'(cfile, objfile)
122 'link_file'(objfile, exefile, extra_obj)
126 die "cannot read infile"
128 die "cannot write outfile"
130 die "cannot close outfile"
137 .local string obj, exe
148 if argc == 2 goto proper_args
149 if argc == 3 goto check_install
153 .local string infile, install
158 if install == '--install' goto proper_install
162 .local string cfile, objfile, exefile
164 cfile = 'replace_pbc_extension'(infile, '.c')
165 objfile = 'replace_pbc_extension'(infile, obj)
166 $S0 = 'replace_pbc_extension'(infile, exe)
167 exefile = concat 'installable_', $S0
169 .return(infile, cfile, objfile, exefile)
176 cfile = 'replace_pbc_extension'(infile, '.c')
177 objfile = 'replace_pbc_extension'(infile, obj)
178 exefile = 'replace_pbc_extension'(infile, exe)
180 # substitute .c for .pbc
181 # remove .c for executable
183 # TODO this should complain about results/returns mismatch
184 .return(infile, cfile, objfile, exefile)
187 .sub 'determine_code_type'
189 .local string gcc_ver
191 .local string os_name
195 gcc_ver = config['gccversion']
196 unless gcc_ver > '' goto not_gcc
201 os_name = config['osname']
203 if os_name != 'MSWin32' goto not_msvc
204 if cc != 'cl' goto not_msvc
215 ifh = open infile, 'r'
216 unless ifh goto err_infile
217 .local string codestring
219 codestring = "const Parrot_UInt1 program_code[] = {"
223 .local string pbcstring
226 pbcstring = read ifh, 16384
227 pbclength = length pbcstring
228 unless pbclength > 0 goto read_done
233 unless pos < pbclength goto code_done
234 $I0 = ord pbcstring, pos
241 unless $I0 == 0 goto code_loop
250 codestring .= "\n};\n\n"
251 codestring .= "const int bytecode_size = "
255 codestring .= <<'END_OF_FUNCTION'
256 const void * get_program_code(void)
265 die "cannot open infile"
269 # The PBC will be represented as a C string, so this sub builds a table
270 # of the C representation of each ASCII character, for lookup by ordinal value.
271 .sub 'generate_encoding_table'
272 # Use '\%o' for speed, or '\x%02x' for readability
273 .const string encoding_format = '\%o'
275 # The 'sprintf' op requires the arglist to be in an array, even when
276 # there is only one arg.
277 .local pmc one_number
278 one_number = new 'FixedIntegerArray'
281 .local pmc coded_strings
282 coded_strings = new 'FixedStringArray'
283 set coded_strings, 256
289 one_number[0] = index
290 $S0 = sprintf encoding_format, one_number
291 coded_strings[index] = $S0
293 if index < 256 goto next_index
295 .return (coded_strings)
298 .sub 'generate_code_gcc'
301 ifh = open infile, 'r'
302 unless ifh goto err_infile
304 .local pmc encoding_table
305 encoding_table = 'generate_encoding_table'()
307 .local string codestring
309 codestring = "const char * program_code =\n"
314 .local string pbcstring
317 pbcstring = read ifh, 16384
318 pbclength = length pbcstring
319 unless pbclength > 0 goto read_done
324 unless pos < pbclength goto code_done
325 $I0 = ord pbcstring, pos
326 $S0 = encoding_table[$I0]
331 unless $I0 == 0 goto code_loop
343 codestring .= "\n;\n\n"
344 codestring .= "const int bytecode_size = "
349 codestring .= <<'END_OF_FUNCTION'
350 const void * get_program_code(void)
359 die "cannot open infile"
363 # Transforms the .pbc path into one with a different extension.
364 # Passing '' means no extension.
365 # Extensions without leading dots will have a dot pre-pended.
366 .sub 'replace_pbc_extension'
367 .param string pbc_path
368 .param string new_extension
370 $S0 = substr pbc_path, -4
372 if $S0 != '.pbc' goto err_pbc_path_not_pbc
373 .local string base_path
374 base_path = substr pbc_path, 0
375 substr base_path, -4, 4, ''
377 .local string new_path
378 new_path = substr base_path, 0
380 unless new_extension > '' goto ext_null
382 $S1 = substr new_extension, 0, 1
383 if $S1 == '.' goto has_dot
387 new_path .= new_extension
392 err_pbc_path_not_pbc:
393 die "input pbc file name does not end in '.pbc'"
397 # In addition to generating the code for inclusion in the C file,
398 # this sub creates supplemental .rc and .RES files.
399 .sub 'generate_code_msvc'
400 .param string pbc_path
402 .local string rc_path
403 .local string res_path
404 rc_path = 'replace_pbc_extension'(pbc_path, '.rc' )
405 res_path = 'replace_pbc_extension'(pbc_path, '.res')
407 # The exact numbers are not relevant;
408 # they are used to identify the resource within the final executable.
409 .local string rc_constant_defines
410 rc_constant_defines = <<'END_OF_DEFINES'
411 #define RESOURCE_NAME_ID_WHOLE_PBC 333
412 #define RESOURCE_TYPE_ID_WHOLE_PBC 444
416 .local string rc_contents
418 rc_contents .= rc_constant_defines
419 rc_contents .= 'RESOURCE_NAME_ID_WHOLE_PBC RESOURCE_TYPE_ID_WHOLE_PBC '
420 rc_contents .= pbc_path
424 rc_fh = open rc_path, 'w'
425 unless rc_fh goto err_rc_open
426 print rc_fh, rc_contents
427 $I0 = rc_fh.'close'()
428 unless $I0 == 0 goto err_rc_close
433 $P2 = $P1.'stat'(pbc_path)
437 .local string codestring
439 codestring .= '#include <windows.h>'
441 codestring .= rc_constant_defines
442 codestring .= "const unsigned int bytecode_size = "
447 codestring .= <<'END_OF_FUNCTION'
448 const void * get_program_code(void)
453 LPVOID actual_pointer_to_pbc_in_memory;
455 hResource = FindResource(
457 MAKEINTRESOURCE(RESOURCE_NAME_ID_WHOLE_PBC),
458 MAKEINTRESOURCE(RESOURCE_TYPE_ID_WHOLE_PBC)
463 size = SizeofResource( NULL, hResource );
464 if (size != bytecode_size)
467 hPBC = LoadResource( NULL, hResource );
471 actual_pointer_to_pbc_in_memory = LockResource( hPBC );
472 if (!actual_pointer_to_pbc_in_memory)
475 return actual_pointer_to_pbc_in_memory;
485 status = spawnw rc_cmd
486 unless status goto rc_ok
488 die "RC command failed"
494 die "cannot open .h file"
496 die "cannot open .rc file"
498 die "cannot close .h file"
500 die "cannot close .rc file"
506 .param string objfile
507 .param int install :optional
510 .local string cc, ccflags, cc_o_out, osname, build_dir, slash
511 .local string installed, includepath, versiondir
513 ccflags = $P0['ccflags']
514 cc_o_out = $P0['cc_o_out']
515 osname = $P0['osname']
516 build_dir = $P0['build_dir']
518 installed = $P0['installed']
519 includepath = $P0['includedir']
520 versiondir = $P0['versiondir']
522 .local string includedir, pathquote
523 if installed == '1' goto installed_includedir
524 includedir = concat build_dir, slash
525 includedir = concat includedir, 'include'
527 installed_includedir:
528 includedir = concat includepath, versiondir
532 unless osname == 'MSWin32' goto not_windows
536 .local string compile
543 compile .= includedir
552 status = spawnw compile
553 unless status goto compiled
555 die "compilation failed"
564 .param string objfile
565 .param string exefile
566 .param string extra_obj
567 .param int install :optional
570 .local string cc, link, link_dynamic, linkflags, ld_out, libparrot, libs, o
571 .local string rpath, osname, build_dir, slash, icushared
572 .local string installed, libdir, versiondir
575 link_dynamic = $P0['link_dynamic']
576 linkflags = $P0['linkflags']
577 ld_out = $P0['ld_out']
578 libparrot = $P0['libparrot_linkflags']
581 rpath = $P0['rpath_blib']
582 osname = $P0['osname']
583 build_dir = $P0['build_dir']
585 icushared = $P0['icu_shared']
586 installed = $P0['installed']
587 libdir = $P0['libdir']
588 versiondir = $P0['versiondir']
590 .local string config, pathquote, exeprefix
591 if installed == '1' goto config_installed
592 exeprefix = substr exefile, 0, 12
593 config = concat build_dir, slash
596 if exeprefix == 'installable_' goto config_to_install
597 config .= 'parrot_config'
600 config .= 'install_config'
601 rpath = $P0['rpath_lib']
604 rpath = $P0['rpath_lib']
605 libparrot = $P0['inst_libparrot_linkflags']
606 config = concat libdir, versiondir
608 config .= 'parrot_config'
612 unless osname == 'MSWin32' goto not_windows
623 unless extra_obj > '' goto skip_extra_obj
647 unless status goto check_manifest
652 # Check if there is a MSVC app manifest
655 .local string manifest_file_name
656 manifest_file_name = exefile
657 manifest_file_name .= '.manifest'
658 .local pmc manifest_exists
659 manifest_exists = file.'exists'( manifest_file_name )
660 unless manifest_exists goto linked
663 # MSVC app manifest exists, embed it
664 .local string embed_manifest_str
665 embed_manifest_str = 'mt.exe -nologo -manifest '
666 embed_manifest_str .= manifest_file_name
667 embed_manifest_str .= ' -outputresource:'
668 embed_manifest_str .= exefile
669 embed_manifest_str .= ';1'
671 say embed_manifest_str
672 .local int embed_manifest_status
673 embed_manifest_status = spawnw embed_manifest_str
674 unless embed_manifest_status goto linked
675 die 'manifest embedding failed'
688 # vim: expandtab shiftwidth=4 ft=pir: