53 using namespace basis;
69 #define BASE_LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT)
70 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
76 #define FAIL_RETURN(retval, where) { \
77 LOG(astring("failure in ") + where + a_sprintf(", exit=%d", retval)); \
93 virtual ~bundled_chunk() {}
108 virtual ~bundle_creator() {
113 virtual int execute();
116 astring determine_stub_file_and_validate();
119 int open_output_file();
127 int write_stub_and_toc();
130 int bundle_sources();
140 int patch_recursive_target(
const astring &source,
const astring &target,
147 int recurse_into_dir(
const astring &source,
const astring &target,
151 int patch_wildcard_target(
const astring &source,
const astring &target,
157 const astring &target,
int manifest_index);
178 %s: This program needs two parameters on the command line.\n\
179 The -o flag must point at the bundled output file to create. The -m flag\n\
180 must point at a valid manifest file that defines what will be packed into\n\
181 the output file. See the example manifest in the bundler example\n\
182 (in setup_src/bundle_example) for more information on the required file\n\
188 int bundle_creator::execute()
192 BASE_LOG(
astring(
"starting file bundling at ") + time_stamp::notarize(
false));
204 if (
filename(_output_file).exists()) {
206 %s: The output file already exists. Please move it out of\n\
207 the way; this program will not overwrite existing files.\n",
212 if (!
filename(_manifest_file).exists()) {
214 %s: The manifest file does not exist. This program cannot do anything\n\
215 without a valid packing manifest.\n", _app_name.s()));
220 astring stub_file_okay = determine_stub_file_and_validate();
221 if (!stub_file_okay) {
223 %s: The unpacking stub file does not exist (check binaries folder).\n\
224 Abandoning bundling process.\n", _app_name.s()));
229 cmds.get_value(
"keyword", _keyword);
237 environment::set(
"EXE_END",
"");
238 environment::set(
"DLL_START",
"lib");
239 environment::set(
"DLL_END",
".so");
241 environment::set(
"EXE_END",
".exe");
242 environment::set(
"DLL_START",
"");
243 environment::set(
"DLL_END",
".dll");
248 environment::set(
"TARGET", environment::TMP());
251 if ( (ret = read_manifest()) )
FAIL_RETURN(ret,
"reading manifest");
253 if ( (ret = open_output_file()) )
FAIL_RETURN(ret,
"opening output file");
255 if ( (ret = write_stub_and_toc()) )
FAIL_RETURN(ret,
"writing stub and TOC");
258 if ( (ret = bundle_sources()) )
FAIL_RETURN(ret,
"bundling source files");
260 if ( (ret = finalize_file()) )
FAIL_RETURN(ret,
"finalizing file");
262 if ( (ret = write_offset()) )
FAIL_RETURN(ret,
"writing offset");
270 int bundle_creator::open_output_file()
274 if (!_bundle->good()) {
275 LOG(
astring(
"failed to open the output file: ") + _output_file);
281 bool bundle_creator::get_file_size(
const astring &infile,
un_int &size,
288 if (!source_file.good()) {
289 LOG(
astring(
"could not access the file for size check: ") + infile);
292 size = int(source_file.length());
298 int bundle_creator::add_files_here(
directory &dirndl,
const astring &source,
299 const astring &target,
int manifest_index)
302 for (
int i = 0; i < dirndl.
files().length(); i++) {
309 bundled_chunk new_guy;
310 new_guy._source = source +
"/" + curry;
311 new_guy._payload = target +
"/" + curry;
312 new_guy._keywords = _manifest_list[manifest_index]._keywords;
314 new_guy._flags = _manifest_list[manifest_index]._flags;
319 bool okaysize = get_file_size(new_guy._source, new_guy._size, new_guy.c_filetime);
320 if (!okaysize || (new_guy._size < 0) ) {
321 LOG(
astring(
"failed to get file size for ") + new_guy._source);
325 _manifest_list.insert(manifest_index + 1, 1);
326 _manifest_list[manifest_index + 1] = new_guy;
331 int bundle_creator::recurse_into_dir(
const astring &source,
332 const astring &target,
int manifest_index)
346 int ret = add_files_here(dirndl, source, target, manifest_index);
358 for (
int i = 0; i < dirs.
length(); i++) {
361 int ret = recurse_into_dir(source +
"/" + s, target +
"/"
362 + s, manifest_index);
363 if (ret != 0)
return ret;
369 int bundle_creator::patch_recursive_target(
const astring &source,
370 const astring &target,
int manifest_index)
372 FUNCDEF(
"patch_recursive_target");
374 return recurse_into_dir(source, target, manifest_index);
377 int bundle_creator::patch_wildcard_target(
const astring &source,
378 const astring &target,
int manifest_index)
380 FUNCDEF(
"patch_wildcard_target");
382 int src_end = source.
end();
383 int slash_indy = source.
find(
'/', src_end,
true);
390 int ret = add_files_here(dirndl, real_source, target, manifest_index);
399 int bundle_creator::read_manifest()
404 bool worked = ini.get_section(
"toc", toc);
406 LOG(
astring(
"failed to read TOC section in manifest:\n") + _manifest_file
407 +
"\ndoes that file exist?");
412 file_logger noisy_logfile(application_configuration::make_logfile_name
413 (
"bundle_creator_activity.log"));
414 noisy_logfile.log(
astring(
'-', 76));
415 noisy_logfile.log(
astring(
"Bundling starts at ") + time_stamp::notarize(
false));
418 _manifest_list.insert(0, toc.
symbols());
420 int final_return = 0;
422 #define BAIL(retval) \
423 final_return = retval; \
425 _manifest_list.zap(i, i); \
429 for (
int i = 0; i < toc.
symbols(); i++) {
433 if (section_name[0] ==
'#') {
436 _manifest_list.zap(i, i);
443 if (ini.get(section_name,
"keyword", value)) {
446 bool worked = list_parsing::parse_csv_line(value, keys);
448 LOG(
astring(
"failed to parse keywords for section ")
449 + section_name +
" in " + _manifest_file);
453 _manifest_list[i]._keywords = keys;
455 list_parsing::create_csv_line(_manifest_list[i]._keywords, dumped);
456 noisy_logfile.log(section_name +
" keywords: " + dumped);
459 if (ini.get(section_name,
"variable", value)) {
465 LOG(
astring(
"failed to parse a variable statement from ") + value);
470 _manifest_list[i]._payload = zohre.
table().
name(0);
471 _manifest_list[i]._parms = zohre.
table()[0];
473 + _manifest_list[i]._parms);
474 astring new_value = parser_bits::substitute_env_vars(_manifest_list[i]._parms);
475 environment::set(_manifest_list[i]._payload, new_value);
478 BASE_LOG(
astring(
"** variable ") + _manifest_list[i]._payload +
" should have value=" + new_value);
479 BASE_LOG(
astring(
"** variable ") + _manifest_list[i]._payload +
" now does have value=" + environment::get(_manifest_list[i]._payload));
483 }
else if (ini.get(section_name,
"assert_defined", value)) {
486 _manifest_list[i]._payload = value;
489 +
"defined at unpacking time.");
493 if (!ini.get(section_name,
"source", _manifest_list[i]._source)) {
495 bool okay_to_omit_source =
false;
497 if (ini.get(section_name,
"no_pack", value)
498 && ini.get(section_name,
"exec_target", value2) ) {
501 okay_to_omit_source =
true;
504 if (!okay_to_omit_source) {
505 LOG(
astring(
"failed to read the source entry for section ")
506 + section_name +
" in " + _manifest_file);
511 _manifest_list[i]._source.replace_all(
'\\',
'/');
513 if (!ini.get(section_name,
"target", _manifest_list[i]._payload)) {
515 bool okay_to_omit_target =
false;
517 if (ini.get(section_name,
"no_pack", value)
518 && ini.get(section_name,
"exec_source", value2) ) {
521 okay_to_omit_target =
true;
524 if (!okay_to_omit_target) {
525 LOG(
astring(
"failed to read the target entry for section ")
526 + section_name +
" in " + _manifest_file);
531 _manifest_list[i]._payload.replace_all(
'\\',
'/');
534 if (ini.get(section_name,
"parms", value)) {
535 _manifest_list[i]._parms = value;
539 if (value[0] !=
'"') {
541 _manifest_list[i]._parms =
astring(
"\"") + value +
"\"";
543 noisy_logfile.log(section_name +
" parms: " + _manifest_list[i]._parms);
547 if (ini.get(section_name,
"error_okay", value)) {
553 if (ini.get(section_name,
"no_replace", value)) {
560 if (ini.get(section_name,
"quiet", value)) {
567 if (ini.get(section_name,
"make_backup", value)) {
573 if (ini.get(section_name,
"recurse", value)) {
580 if (ini.get(section_name,
"no_pack", value)) {
587 if (ini.get(section_name,
"exec_source", value)) {
594 if (ini.get(section_name,
"exec_target", value)) {
602 _manifest_list[i]._source = parser_bits::substitute_env_vars
603 (_manifest_list[i]._source,
false);
606 int indy = _manifest_list[i]._source.find(
"*");
610 if (!!_keyword && !_manifest_list[i]._keywords.member(_keyword)) {
612 noisy_logfile.log(
astring(
"skipping ") + _manifest_list[i]._payload
613 +
" file check; doesn't match keyword \"" + _keyword +
"\"");
623 byte_filer source_file(_manifest_list[i]._source,
"rb");
624 if (!source_file.good()) {
625 LOG(
astring(
"could not access the source file for bundling: ")
626 + _manifest_list[i]._source);
629 bool okaysize = get_file_size(_manifest_list[i]._source,
630 _manifest_list[i]._size, _manifest_list[i].c_filetime);
631 if (!okaysize || (_manifest_list[i]._size < 0) ) {
639 for (
int i = 0; i < _manifest_list.length(); i++) {
640 bundled_chunk curr = _manifest_list[i];
642 if (!!_keyword && !curr._keywords.member(_keyword)) {
644 noisy_logfile.log(
astring(
"zapping entry for ") + curr._payload
645 +
"; doesn't match keyword \"" + _keyword +
"\"");
646 _manifest_list.zap(i, i);
659 int star_indy = curr._source.find(
"*");
662 LOG(
astring(
"illegal combination of recursion and wildcard: ")
667 int ret = patch_recursive_target(curr._source, curr._payload, i);
669 LOG(
astring(
"failed during packing of recursive source: ")
674 _manifest_list.zap(i, i);
682 curr._parms = parser_bits::substitute_env_vars(curr._parms,
false);
687 basis::un_int retval = launch_process::run(curr._source, curr._parms,
688 launch_process::AWAIT_APP_EXIT, kid);
690 LOG(
astring(
"failed to launch process, source=") + curr._source
691 +
", with parms " + curr._parms);
699 _manifest_list.zap(i, i);
705 int star_indy = curr._source.find(
"*");
709 int slash_indy = curr._source.find(
'/', curr._source.end(),
true);
710 if (star_indy < slash_indy) {
712 BASE_LOG(
astring(
" (the wildcard must be in the last component of the path)"));
716 int ret = patch_wildcard_target(curr._source, curr._payload, i);
718 LOG(
astring(
"failed during packing of wildcarded source: ")
722 _manifest_list.zap(i, i);
731 LOG(
"read the following info from manifest:");
732 for (
int i = 0; i < _manifest_list.length(); i++) {
733 bundled_chunk &curr = _manifest_list[i];
735 curr._source.s(), curr._payload.s()));
743 astring bundle_creator::determine_stub_file_and_validate()
745 FUNCDEF(
"determine_stub_file_and_validate");
749 astring stub_filename(
"unpacker_stub");
752 astring stub_filename(
"unpacker_stub.exe");
754 astring repo_dir =
"$RUNTIME_PATH";
755 astring stub_file = parser_bits::substitute_env_vars
756 (repo_dir +
"/binaries/" + stub_filename,
false);
757 if (!
filename(stub_file).exists()) {
759 LOG(
astring(
"could not find unpacking stub file at: ") + stub_file);
760 return astring::empty_string();
765 int bundle_creator::write_stub_and_toc()
769 astring stub_file = determine_stub_file_and_validate();
770 if (!stub_file)
return 1;
774 if (!stubby.good()) {
777 _stub_size = int(stubby.length());
779 stubby.read(whole_stub, _stub_size + 100);
781 _bundle->write(whole_stub);
785 int ret = _bundle->write(packed_toc_len);
787 LOG(
astring(
"could not write the TOC length to the bundle: ")
793 for (
int i = 0; i < _manifest_list.length(); i++) {
794 bundled_chunk &curr = _manifest_list[i];
798 if (_bundle->write(chunk) <= 0) {
799 LOG(
a_sprintf(
"could not write item #%d [%s] to the bundle: ", i,
809 int bundle_creator::bundle_sources()
813 file_logger noisy_logfile(application_configuration::make_logfile_name
814 (
"bundle_creator_activity.log"));
815 for (
int i = 0; i < _manifest_list.length(); i++) {
816 bundled_chunk &curr = _manifest_list[i];
820 noisy_logfile.log(
astring(
"bundling: variable setting ") + curr._payload
821 +
"=" + curr._parms);
825 noisy_logfile.log(
astring(
"bundling: test variable ") + curr._payload
833 noisy_logfile.log(
astring(
"bundling: ") + curr._source);
835 if (!source.good()) {
836 LOG(
a_sprintf(
"could not read item #%d for the bundle: \"", i)
837 + curr._source +
"\"");
846 int total_written = 0;
850 LOG(
a_sprintf(
"failed while reading item #%d: ", i) + curr._source);
853 total_written += ret;
856 bool null_chunk =
false;
861 compressed.reset(
int(0.1 * ret) + ret +
KILOBYTE);
864 destlen = compressed.length();
866 int comp_ret = compress(compressed.access(), &destlen, temp.
observe(),
868 if (comp_ret != Z_OK) {
873 compressed.zap(destlen, compressed.length() - 1);
880 ret = _bundle->write(just_sizes);
882 LOG(
a_sprintf(
"failed while writing sizes for item #%d: ", i)
887 ret = _bundle->write(compressed);
889 LOG(
a_sprintf(
"failed while writing item #%d: ", i) + curr._source);
891 }
else if (ret != compressed.length()) {
892 LOG(
a_sprintf(
"wrote different size for item #%d (tried %d, "
893 "wrote %d): ", i, compressed.length(), ret) + curr._source);
897 }
while (!source.eof());
902 int ret = _bundle->write(just_sizes);
904 LOG(
a_sprintf(
"failed while writing sentinel of item #%d: ", i)
909 if (total_written != curr._size) {
910 LOG(
a_sprintf(
"size (%d) disagrees with initial size (%d) for "
911 "item #%d: ", total_written, curr._size, i) + curr._source);
914 noisy_logfile.log(
astring(
"Bundling run ends at ") + time_stamp::notarize(
false));
915 noisy_logfile.log(
astring(
'-', 76));
920 int bundle_creator::finalize_file()
926 int bundle_creator::write_offset()
931 astring magic_string(
"muftiloc");
936 bool found_it =
false;
938 for (
int i = 0; i < magic_string.length(); i++) {
939 int ret = bun.read(temp_string, 1);
941 if (temp_string[0] != magic_string[i])
break;
942 if (i == magic_string.end()) {
945 location = int(bun.tell());
949 if (!found_it)
continue;
956 bun.write(packed_offset);
962 chmod(_output_file.s(), 0766);
965 BASE_LOG(
astring(
"done file bundling at ") + time_stamp::notarize(
false));
974 #ifdef __BUILD_STATIC_APPLICATION__
const astring SUBVERSION_FOLDER
#define BASE_LOG(to_print)
bool true_value(const astring &value)
#define FAIL_RETURN(retval, where)
int print_instructions(bool good, const astring &program_name)
The application_shell is a base object for console programs.
a_sprintf is a specialization of astring that provides printf style support.
const contents * observe() const
Returns a pointer to the underlying C array of data.
int length() const
Returns the current reported length of the allocated C array.
Provides a dynamically resizable ASCII character string.
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
bool substring(astring &target, int start, int end) const
a version that stores the substring in an existing "target" string.
void strip_spaces(how_to_strip way=FROM_BOTH_SIDES)
removes excess space characters from string's beginning, end or both.
bool equal_to(const char *that) const
returns true if "that" is equal to this.
int end() const
returns the index of the last (non-null) character in the string.
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
bool contains(const astring &to_find) const
Returns true if "to_find" is contained in this string or false if not.
A very common template for a dynamic array of bytes.
Supports a configurator-based interface on text initialization files.
Manages a bank of textual definitions of variables.
const structures::string_table & table() const
provides a constant peek at the string_table holding the values.
int symbols() const
returns the number of entries in the variable_tokenizer.
bool parse(const basis::astring &to_tokenize)
parses the string using our established sentinel characters.
Provides file managment services using the standard I/O support.
Implements a scanner that finds all filenames in the directory specified.
const structures::string_array & directories() const
these are the directory names from the folder.
const structures::string_array & files() const
returns the list of files that we found in this directory.
Provides operations commonly needed on file names.
An array of strings with some additional helpful methods.
Provides a symbol_table that holds strings as the content.
basis::outcome zap_index(int index)
zaps the entry at the specified index. slower than whack().
const basis::astring & name(int index) const
returns the name held at the "index".
int symbols() const
returns the number of symbols listed in the table.
Represents a point in time relative to the operating system startup time.
void reset()
sets the stamp time back to now.
@ TEST_VARIABLE_DEFINED
check for required variable's presence.
@ OMIT_PACKING
for a source side exe, do not pack the file.
@ SET_VARIABLE
this item just has a variable assignment.
@ RECURSIVE_SRC
source is a recursive folder.
@ SOURCE_EXECUTE
the file should be executed before bundling.
@ QUIET_FAILURE
when errors happen, no popup message happens.
@ IGNORE_ERRORS
if set, errors in an item will not stop program.
@ TARGET_EXECUTE
the file should be executed on unbundling.
@ MAKE_BACKUP_FILE
save a copy if original file already exists.
@ NO_OVERWRITE
target file will not be overwritten if exists.
#define NULL_POINTER
The value representing a pointer to nothing.
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
bool non_negative(const type &a)
non_negative returns true if "a" is greater than or equal to zero.
unsigned int un_int
Abbreviated name for unsigned integers.
bool negative(const type &a)
negative returns true if "a" is less than zero.
const int KILOBYTE
Number of bytes in a kilobyte.
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
void obscure_attach(byte_array &packed_form, un_int to_attach)
like the normal attach but shifts in some recognizable sentinel data.
we will read the manifest pieces out of our own exe image.