From 360a9858e33fc83763b0d82d16a2e2b331df5550 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Sun, 1 Oct 2017 02:42:02 +0200 Subject: [PATCH 01/29] Automated tests! --- AutomatedTests/CMakeLists.txt | 20 ++++ AutomatedTests/Empty.c | 4 + AutomatedTests/File.c | 6 + AutomatedTests/ReallyEmpty.c | 4 + AutomatedTests/Test.c | 6 + AutomatedTests/Test.h | 40 +++++++ AutomatedTests/Timeout.c | 11 ++ CMakeLists.txt | 4 + LaunchAPPL/CMakeLists.txt | 7 ++ LaunchAPPL/LaunchAPPL.cc | 215 ++++++++++++++++++++++++++++++++++ 10 files changed, 317 insertions(+) create mode 100644 AutomatedTests/CMakeLists.txt create mode 100644 AutomatedTests/Empty.c create mode 100644 AutomatedTests/File.c create mode 100644 AutomatedTests/ReallyEmpty.c create mode 100644 AutomatedTests/Test.c create mode 100644 AutomatedTests/Test.h create mode 100644 AutomatedTests/Timeout.c create mode 100644 LaunchAPPL/CMakeLists.txt create mode 100644 LaunchAPPL/LaunchAPPL.cc diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt new file mode 100644 index 0000000000..4378cddb18 --- /dev/null +++ b/AutomatedTests/CMakeLists.txt @@ -0,0 +1,20 @@ +enable_testing() + +add_application(ReallyEmpty ReallyEmpty.c) +add_test(NAME ReallyEmpty COMMAND LaunchAPPL -e ReallyEmpty.bin) + +add_application(Empty Empty.c) +add_test(NAME Empty COMMAND LaunchAPPL -e Empty.bin) + +add_application(File File.c) +add_test(NAME File + COMMAND LaunchAPPL -e File.bin --logfile=out -t5 + ) +set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") + +add_application(Timeout Timeout.c) +add_test(NAME Timeout + COMMAND sh -c "! LaunchAPPL -e Timeout.bin --logfile=out -t2" + ) +set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One;Two" + FAIL_REGULAR_EXPRESSION "Three") diff --git a/AutomatedTests/Empty.c b/AutomatedTests/Empty.c new file mode 100644 index 0000000000..a46866d92e --- /dev/null +++ b/AutomatedTests/Empty.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/AutomatedTests/File.c b/AutomatedTests/File.c new file mode 100644 index 0000000000..1c2595a4b5 --- /dev/null +++ b/AutomatedTests/File.c @@ -0,0 +1,6 @@ +#include "Test.h" + +int main() +{ + TEST_LOG_SIZED("OK", 2); +} diff --git a/AutomatedTests/ReallyEmpty.c b/AutomatedTests/ReallyEmpty.c new file mode 100644 index 0000000000..81e5f841aa --- /dev/null +++ b/AutomatedTests/ReallyEmpty.c @@ -0,0 +1,4 @@ +void _start() +{ +} + diff --git a/AutomatedTests/Test.c b/AutomatedTests/Test.c new file mode 100644 index 0000000000..5b25106471 --- /dev/null +++ b/AutomatedTests/Test.c @@ -0,0 +1,6 @@ +#include "Test.h" + +void TestLog(const char *str) +{ + TEST_LOG_SIZED(str, strlen(str)); +} diff --git a/AutomatedTests/Test.h b/AutomatedTests/Test.h new file mode 100644 index 0000000000..f8970d0f4d --- /dev/null +++ b/AutomatedTests/Test.h @@ -0,0 +1,40 @@ +#ifndef TEST_H +#define TEST_H + +#include +#include + +#define TEST_LOG_SIZED(str, size) \ + do { \ + HParamBlockRec _hpb; \ + \ + unsigned char _fileName[4]; \ + _fileName[0] = 3; \ + _fileName[1] = 'o'; \ + _fileName[2] = 'u'; \ + _fileName[3] = 't'; \ + \ + _hpb.ioParam.ioCompletion = NULL; \ + _hpb.ioParam.ioNamePtr = (StringPtr)_fileName; \ + _hpb.ioParam.ioVRefNum = 0; \ + _hpb.fileParam.ioDirID = 0; \ + _hpb.ioParam.ioPermssn = fsRdWrPerm; \ + PBHOpenSync(&_hpb); \ + \ + _hpb.ioParam.ioBuffer = str; \ + _hpb.ioParam.ioReqCount = size; \ + _hpb.ioParam.ioPosMode = fsFromLEOF; \ + _hpb.ioParam.ioPosOffset = 0; \ + PBWriteSync((void*)&_hpb); \ + char _newline = '\n'; \ + _hpb.ioParam.ioBuffer = &_newline; \ + _hpb.ioParam.ioReqCount = 1; \ + _hpb.ioParam.ioPosMode = fsFromLEOF; \ + _hpb.ioParam.ioPosOffset = 0; \ + PBWriteSync((void*)&_hpb); \ + PBCloseSync((void*)&_hpb); \ + } while(0); + +void TestLog(const char *str); + +#endif // TEST_H diff --git a/AutomatedTests/Timeout.c b/AutomatedTests/Timeout.c new file mode 100644 index 0000000000..ec6624054c --- /dev/null +++ b/AutomatedTests/Timeout.c @@ -0,0 +1,11 @@ +#include "Test.h" + +int main() +{ + TEST_LOG_SIZED("One",3); + TEST_LOG_SIZED("Two",3); + for(;;) + ; + TEST_LOG_SIZED("Three",5); + return 0; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index b65711f06c..d5514ae520 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,9 @@ add_subdirectory(Samples/Launcher) add_subdirectory(Samples/SystemExtension) endif() +enable_testing() +add_subdirectory(AutomatedTests) + else() set(RETRO68_ROOT ${CMAKE_INSTALL_PREFIX}) @@ -62,4 +65,5 @@ add_subdirectory(Rez) add_subdirectory(ConvertObj) add_subdirectory(PEFTools) add_subdirectory(Elf2Mac) +add_subdirectory(LaunchAPPL) endif() diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt new file mode 100644 index 0000000000..1e98c88d02 --- /dev/null +++ b/LaunchAPPL/CMakeLists.txt @@ -0,0 +1,7 @@ +find_package(Boost COMPONENTS filesystem program_options) + +add_executable(LaunchAPPL LaunchAPPL.cc) +target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) +target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) + +install(TARGETS LaunchAPPL RUNTIME DESTINATION bin) diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc new file mode 100644 index 0000000000..626a3ee75f --- /dev/null +++ b/LaunchAPPL/LaunchAPPL.cc @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include "ResourceFork.h" +#include "ResourceFile.h" + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +using std::string; +using std::vector; + +static po::options_description desc; +po::variables_map options; + +static void usage() +{ + std::cerr << "Usage: " << "LaunchAPPL [options] appl-file\n"; + std::cerr << desc << std::endl; +} + + +int ChildProcess(string program, vector args) +{ + std::vector argv; + argv.push_back(program.c_str()); + for(string& s : args) + argv.push_back(s.c_str()); + argv.push_back(NULL); + + int timeout = options.count("timeout") ? options["timeout"].as() : 0; + + pid_t pid = fork(); + if(pid < 0) + { + perror("unable to fork"); + return 1; + } + else if(pid == 0) + { + pid_t worker_pid = timeout ? fork() : 0; + if(worker_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(worker_pid == 0) + { + execvp(argv[0], const_cast (argv.data())); + perror("exec failed"); + _exit(1); + } + + pid_t timeout_pid = fork(); + if(timeout_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(timeout_pid == 0) + { + sleep(timeout); + _exit(0); + } + int wstatus; + pid_t exited_pid = wait(&wstatus); + if(exited_pid == worker_pid) + { + kill(timeout_pid, SIGKILL); + wait(NULL); + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + _exit(exitcode); + } + } + else + { + kill(worker_pid, SIGKILL); + wait(NULL); + _exit(1); + } + } + else + { + int wstatus; + int result = 0; + do + { + result = waitpid(pid, &wstatus, 0); + } while(result == -1 && errno == EINTR); + + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + return exitcode; + } + } + +} + +int main(int argc, char *argv[]) +{ + desc.add_options() + ("help,h", "show this help message") + ("executor-path", po::value()->default_value("executor"),"path to executor") + ("executor,e", "run using executor") + ("timeout,t", po::value(),"abort after timeout") + ("logfile", po::value(), "read log file") + ("result,r", "TEST 128") + ; + po::options_description hidden, alldesc; + hidden.add_options() + ("application,a", po::value(), "application" ) + ; + alldesc.add(desc).add(hidden); + + try + { + auto parsed = po::command_line_parser(argc, argv) + .options(alldesc) + .positional(po::positional_options_description().add("application", -1)) + .style(po::command_line_style::default_style) + .run(); + + po::store(parsed, options); + } + catch(po::error& e) + { + std::cerr << "ERROR: " << e.what() << std::endl << std::endl; + usage(); + return 1; + } + + po::notify(options); + + if(options.count("help") || !options.count("application")) + { + usage(); + return 0; + } + + ResourceFile app(options["application"].as()); + if(!app.read()) + { + std::cerr << "Could not read application file.\n"; + return 1; + } + + + + if(options.count("executor")) + { + fs::path tempDir = fs::unique_path(); + std::cerr << "Unique path: " << tempDir.string() << std::endl; + fs::create_directories(tempDir); + + fs::path appPath = tempDir / "Application"; + + app.assign(appPath.string(), ResourceFile::Format::percent_appledouble); + if(!app.write()) + { + std::cerr << "Could not write application file.\n"; + return 1; + } + + if(options.count("logfile")) + { + fs::ofstream out(tempDir/options["logfile"].as()); + } + + int result = ChildProcess(options["executor-path"].as(), { appPath.string() }); + + if(options.count("logfile")) + { + fs::ifstream in(tempDir/options["logfile"].as()); + std::cout << in.rdbuf(); + } + + + if(result == 0 && options.count("result")) + { + app.read(); + auto& resmap = app.resources.resources; + auto p = resmap.find(ResRef("TEST", 128)); + if(p == resmap.end()) + return 1; + + std::cout << p->second.getData(); + } + + fs::remove_all(tempDir); + + return result; + } + + return 0; +} From 270cafaab4612dde9d345f5899612f88fd2f2e1c Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Sun, 1 Oct 2017 23:36:50 +0200 Subject: [PATCH 02/29] improve LaunchAPPL --- AutomatedTests/Log.c | 11 +++ AutomatedTests/Test.h | 13 ++- Dockerfile | 15 ++++ LaunchAPPL/CMakeLists.txt | 2 +- LaunchAPPL/LaunchAPPL.cc | 176 ++++++++++++++++++++++++++++++++++---- LaunchAPPL/bootblock.c | 67 +++++++++++++++ 6 files changed, 266 insertions(+), 18 deletions(-) create mode 100644 AutomatedTests/Log.c create mode 100644 Dockerfile create mode 100644 LaunchAPPL/bootblock.c diff --git a/AutomatedTests/Log.c b/AutomatedTests/Log.c new file mode 100644 index 0000000000..ec6624054c --- /dev/null +++ b/AutomatedTests/Log.c @@ -0,0 +1,11 @@ +#include "Test.h" + +int main() +{ + TEST_LOG_SIZED("One",3); + TEST_LOG_SIZED("Two",3); + for(;;) + ; + TEST_LOG_SIZED("Three",5); + return 0; +} diff --git a/AutomatedTests/Test.h b/AutomatedTests/Test.h index f8970d0f4d..46c3b66940 100644 --- a/AutomatedTests/Test.h +++ b/AutomatedTests/Test.h @@ -3,12 +3,15 @@ #include #include +#include #define TEST_LOG_SIZED(str, size) \ do { \ HParamBlockRec _hpb; \ + memset(&_hpb,0,sizeof(_hpb)); \ \ unsigned char _fileName[4]; \ + short _ref;\ _fileName[0] = 3; \ _fileName[1] = 'o'; \ _fileName[2] = 'u'; \ @@ -20,19 +23,27 @@ _hpb.fileParam.ioDirID = 0; \ _hpb.ioParam.ioPermssn = fsRdWrPerm; \ PBHOpenSync(&_hpb); \ - \ + _ref = _hpb.ioParam.ioRefNum; \ + \ + memset(&_hpb,0,sizeof(_hpb)); \ _hpb.ioParam.ioBuffer = str; \ _hpb.ioParam.ioReqCount = size; \ _hpb.ioParam.ioPosMode = fsFromLEOF; \ _hpb.ioParam.ioPosOffset = 0; \ + _hpb.ioParam.ioRefNum = _ref; \ PBWriteSync((void*)&_hpb); \ + memset(&_hpb,0,sizeof(_hpb)); \ char _newline = '\n'; \ _hpb.ioParam.ioBuffer = &_newline; \ _hpb.ioParam.ioReqCount = 1; \ _hpb.ioParam.ioPosMode = fsFromLEOF; \ _hpb.ioParam.ioPosOffset = 0; \ + _hpb.ioParam.ioRefNum = _ref; \ PBWriteSync((void*)&_hpb); \ + memset(&_hpb,0,sizeof(_hpb)); \ + _hpb.ioParam.ioRefNum = _ref; \ PBCloseSync((void*)&_hpb); \ + FlushVol(NULL,0); \ } while(0); void TestLog(const char *str); diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..87342f2649 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# vim:ft=dockerfile +FROM ubuntu:16.04 + +RUN apt-get update && apt-get -y install \ + g++ \ + cmake libgmp-dev libmpfr-dev libmpc-dev libboost-all-dev bison \ + zlib1g-dev \ + perl texinfo + +RUN mkdir /root/Retro68 +COPY . /root/Retro68/ + +RUN mkdir /root/Retro68-build + +RUN sh -c "cd /root/Retro68-build && sh ../Retro68/build-toolchain.sh --clean-after-build" \ No newline at end of file diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 1e98c88d02..14f268d3fd 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(Boost COMPONENTS filesystem program_options) -add_executable(LaunchAPPL LaunchAPPL.cc) +add_executable(LaunchAPPL LaunchAPPL.cc bootblock.c) target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 626a3ee75f..e9288c78b7 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -13,6 +13,11 @@ #include "ResourceFork.h" #include "ResourceFile.h" +extern "C" { +#include "hfs.h" +} + + namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -119,12 +124,18 @@ int ChildProcess(string program, vector args) int main(int argc, char *argv[]) { desc.add_options() - ("help,h", "show this help message") - ("executor-path", po::value()->default_value("executor"),"path to executor") + ("help,h", "show this help message") ("executor,e", "run using executor") + ("minivmac,m", "run using executor") + + ("executor-path", po::value()->default_value("executor"),"path to executor") + ("minivmac-path", po::value()->default_value("minivmac"),"path to minivmac") + ("minivmac-dir", po::value()->default_value("."),"directory containing vMac.ROM") + ("system-image", po::value(),"path to disk image with system") + ("timeout,t", po::value(),"abort after timeout") + ("timeout-ok","timeout counts as success") ("logfile", po::value(), "read log file") - ("result,r", "TEST 128") ; po::options_description hidden, alldesc; hidden.add_options() @@ -169,7 +180,6 @@ int main(int argc, char *argv[]) if(options.count("executor")) { fs::path tempDir = fs::unique_path(); - std::cerr << "Unique path: " << tempDir.string() << std::endl; fs::create_directories(tempDir); fs::path appPath = tempDir / "Application"; @@ -194,22 +204,156 @@ int main(int argc, char *argv[]) std::cout << in.rdbuf(); } - - if(result == 0 && options.count("result")) - { - app.read(); - auto& resmap = app.resources.resources; - auto p = resmap.find(ResRef("TEST", 128)); - if(p == resmap.end()) - return 1; - - std::cout << p->second.getData(); - } - fs::remove_all(tempDir); return result; } + if(options.count("minivmac")) + { + assert(options.count("system-image")); + fs::path tempDir = fs::unique_path(); + fs::path path = tempDir / "image.dsk"; + fs::create_directories(tempDir); + + hfsvol *sysvol = hfs_mount(options["system-image"].as().c_str(), + 0, HFS_MODE_RDONLY); + + int size = 5000*1024; + fs::ofstream(path, std::ios::binary | std::ios::trunc).seekp(size-1).put(0); + hfs_format(path.string().c_str(), 0, 0, "SysAndApp", 0, NULL); + hfsvol *vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDWR); + + hfsvolent ent; + hfs_vstat(sysvol, &ent); + + hfs_setcwd(sysvol, ent.blessed); + + + + + { + const char *fn = "System"; + hfsdirent fileent; + hfs_stat(sysvol, fn, &fileent); + hfsfile *in = hfs_open(sysvol, fn); + hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); + + std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); + hfs_setfork(in, 0); + hfs_setfork(out, 0); + hfs_read(in, buffer.data(), fileent.u.file.dsize); + hfs_write(out, buffer.data(), fileent.u.file.dsize); + hfs_setfork(in, 1); + hfs_setfork(out, 1); + hfs_read(in, buffer.data(), fileent.u.file.rsize); + hfs_write(out, buffer.data(), fileent.u.file.rsize); + hfs_close(in); + hfs_close(out); + } + { + const char *fn = "Finder"; + hfsdirent fileent; + hfs_stat(sysvol, fn, &fileent); + hfsfile *in = hfs_open(sysvol, fn); + hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); + + std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); + hfs_setfork(in, 0); + hfs_setfork(out, 0); + hfs_read(in, buffer.data(), fileent.u.file.dsize); + hfs_write(out, buffer.data(), fileent.u.file.dsize); + hfs_setfork(in, 1); + hfs_setfork(out, 1); + hfs_read(in, buffer.data(), fileent.u.file.rsize); + hfs_write(out, buffer.data(), fileent.u.file.rsize); + hfs_close(in); + hfs_close(out); + } + { + const char *fn = "MacsBug"; + hfsdirent fileent; + hfs_stat(sysvol, fn, &fileent); + hfsfile *in = hfs_open(sysvol, fn); + hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); + + std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); + hfs_setfork(in, 0); + hfs_setfork(out, 0); + hfs_read(in, buffer.data(), fileent.u.file.dsize); + hfs_write(out, buffer.data(), fileent.u.file.dsize); + hfs_setfork(in, 1); + hfs_setfork(out, 1); + hfs_read(in, buffer.data(), fileent.u.file.rsize); + hfs_write(out, buffer.data(), fileent.u.file.rsize); + hfs_close(in); + hfs_close(out); + } + + { + std::ostringstream rsrcOut; + app.resources.writeFork(rsrcOut); + std::string rsrc = rsrcOut.str(); + std::string& data = app.data; + + hfsfile *file = hfs_create(vol, "App","APPL","????"); + hfs_setfork(file, 0); + hfs_write(file, data.data(), data.size()); + hfs_setfork(file, 1); + hfs_write(file, rsrc.data(), rsrc.size()); + hfs_close(file); + } + + { + hfsfile *out = hfs_create(vol, "out", "TEXT", "????"); + hfs_close(out); + } + + hfs_vstat(vol, &ent); + ent.blessed = hfs_getcwd(vol); + std::cout << "blessed: " << ent.blessed << std::endl; + hfs_vsetattr(vol, &ent); + + hfs_umount(vol); + hfs_umount(sysvol); + + extern unsigned char bootblock[1024]; + std::vector bootblock1(bootblock, bootblock+1024); + std::fstream out(path.string(), std::ios::in | std::ios::out | std::ios::binary); + + bootblock1[0x5A] = 3; + bootblock1[0x5B] = 'A'; + bootblock1[0x5C] = 'p'; + bootblock1[0x5D] = 'p'; + + out.write((const char*) bootblock1.data(), 1024); + + path = fs::absolute(path); + + fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); + fs::path minivmacpath = fs::absolute( minivmacdir / options["minivmac-path"].as() ); + + fs::current_path(minivmacdir); + + int result = ChildProcess(minivmacpath.string(), { path.string() }); + + std::cerr << "volume at: " << path.string() << std::endl; + vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDONLY); + { + hfsfile *out = hfs_open(vol, "out"); + if(!out) + return 1; + hfsdirent fileent; + int statres = hfs_stat(vol, "out", &fileent); + std::cerr << "stat: " << statres << "\n"; + std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; + std::vector buffer(fileent.u.file.dsize); + hfs_setfork(out, 0); + hfs_read(out, buffer.data(), fileent.u.file.dsize); + hfs_close(out); + std::cout << string(buffer.begin(), buffer.end()); + } + hfs_umount(vol); + } return 0; } diff --git a/LaunchAPPL/bootblock.c b/LaunchAPPL/bootblock.c new file mode 100644 index 0000000000..fa1a23eb89 --- /dev/null +++ b/LaunchAPPL/bootblock.c @@ -0,0 +1,67 @@ +unsigned char bootblock[] = { +/* 00000000 */ 0x4c, 0x4b, 0x60, 0x00, 0x00, 0x86, 0x00, 0x17, 0x00, 0x00, 0x06, 0x53, 0x79, 0x73, 0x74, 0x65, /* |LK`........Syste| */ +/* 00000010 */ 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x06, 0x46, 0x69, 0x6e, 0x64, 0x65, /* |m .Finde| */ +/* 00000020 */ 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x07, 0x4d, 0x61, 0x63, 0x73, 0x62, /* |r .Macsb| */ +/* 00000030 */ 0x75, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0c, 0x44, 0x69, 0x73, 0x61, 0x73, /* |ug .Disas| */ +/* 00000040 */ 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x20, 0x20, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, /* |sembler .Start| */ +/* 00000050 */ 0x55, 0x70, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x20, 0x06, 0x46, 0x69, 0x6e, 0x64, 0x65, /* |UpScreen .Finde| */ +/* 00000060 */ 0x72, 0x00, 0x81, 0x00, 0x00, 0x72, 0x00, 0x00, 0x64, 0x0c, 0x0e, 0x43, 0x6c, 0x69, 0x70, 0x62, /* |r....r..d..Clipb| */ +/* 00000070 */ 0x6f, 0x61, 0x72, 0x64, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, /* |oard File ......| */ +/* 00000080 */ 0x43, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x22, 0x38, 0x01, 0x08, 0x48, 0x41, /* |C........."8..HA| */ +/* 00000090 */ 0x0c, 0x41, 0x00, 0x04, 0x6e, 0x2c, 0x72, 0x00, 0x50, 0xf9, 0x00, 0x01, 0xff, 0xf0, 0x42, 0xb9, /* |.A..n,r.P.....B.| */ +/* 000000a0 */ 0x00, 0x03, 0xff, 0xf0, 0x4a, 0xb9, 0x00, 0x01, 0xff, 0xf0, 0x67, 0x16, 0x70, 0x02, 0x48, 0x40, /* |....J.....g.p.H@| */ +/* 000000b0 */ 0xd1, 0xb8, 0x01, 0x08, 0xd1, 0xb8, 0x08, 0x24, 0xd1, 0xb8, 0x02, 0x66, 0xd1, 0xb8, 0x01, 0x0c, /* |.......$...f....| */ +/* 000000c0 */ 0x72, 0x04, 0x20, 0x78, 0x02, 0xa6, 0x0c, 0x41, 0x00, 0x08, 0x6f, 0x02, 0x72, 0x08, 0xd1, 0xfb, /* |r. x...A..o.r...| */ +/* 000000d0 */ 0x10, 0xae, 0x2f, 0x08, 0x70, 0x07, 0x41, 0xf8, 0x0a, 0xb8, 0x42, 0x98, 0x51, 0xc8, 0xff, 0xfc, /* |../.p.A...B.Q...| */ +/* 000000e0 */ 0x34, 0x3a, 0xff, 0x9a, 0x70, 0x16, 0xc0, 0xc2, 0x22, 0x00, 0xa7, 0x1e, 0x43, 0xf8, 0x01, 0x54, /* |4:..p..."...C..T| */ +/* 000000f0 */ 0x53, 0x42, 0x32, 0x82, 0x42, 0xa1, 0x42, 0xa1, 0x42, 0x61, 0x23, 0x08, 0x46, 0x58, 0x55, 0x41, /* |SB2.B.B.Ba#.FXUA| */ +/* 00000100 */ 0x66, 0xfa, 0x33, 0x3c, 0xff, 0xef, 0x42, 0x78, 0x01, 0x84, 0x72, 0xfc, 0x70, 0x0f, 0x14, 0x38, /* |f.3<..Bx..r.p..8| */ +/* 00000110 */ 0x02, 0x06, 0xc0, 0x02, 0xd0, 0x40, 0x48, 0x40, 0x10, 0x02, 0xe4, 0x48, 0xc0, 0x41, 0x48, 0x40, /* |.....@H@...H.AH@| */ +/* 00000120 */ 0x21, 0xc0, 0x01, 0x8e, 0x70, 0x0f, 0x14, 0x38, 0x02, 0x09, 0xc0, 0x02, 0xe5, 0x48, 0x21, 0xc0, /* |!...p..8.....H!.| */ +/* 00000130 */ 0x02, 0xf4, 0x10, 0x02, 0xe4, 0x48, 0xc0, 0x41, 0x21, 0xc0, 0x02, 0xf0, 0x41, 0xf8, 0x03, 0x40, /* |.....H.A!...A..@| */ +/* 00000140 */ 0x72, 0x50, 0x42, 0x58, 0x51, 0xc9, 0xff, 0xfc, 0x70, 0x1e, 0xc0, 0xfa, 0xff, 0x2e, 0x32, 0x38, /* |rPBXQ...p.....28| */ +/* 00000150 */ 0x01, 0x08, 0xe2, 0x49, 0xc0, 0xc1, 0x54, 0x40, 0x32, 0x00, 0xa7, 0x1e, 0x21, 0xc8, 0x03, 0x4e, /* |...I..T@2...!..N| */ +/* 00000160 */ 0x30, 0xc1, 0x31, 0xfc, 0x00, 0x02, 0x03, 0x4c, 0x9e, 0xfc, 0x00, 0x32, 0x20, 0x4f, 0x31, 0x78, /* |0.1....L...2 O1x| */ +/* 00000170 */ 0x02, 0x10, 0x00, 0x16, 0xa0, 0x0f, 0x66, 0x00, 0x01, 0xb2, 0xde, 0xfc, 0x00, 0x32, 0x43, 0xf8, /* |......f......2C.| */ +/* 00000180 */ 0x0a, 0xd8, 0x41, 0xfa, 0xfe, 0x86, 0x70, 0x10, 0xa0, 0x2e, 0x55, 0x4f, 0x2f, 0x0f, 0x48, 0x78, /* |..A...p...UO/.Hx| */ +/* 00000190 */ 0x09, 0xfa, 0x20, 0x78, 0x08, 0x10, 0x4e, 0x90, 0x30, 0x1f, 0xe6, 0x48, 0x31, 0xc0, 0x01, 0x06, /* |.. x..N.0..H1...| */ +/* 000001a0 */ 0x08, 0x38, 0x00, 0x06, 0x02, 0x0b, 0x56, 0xf8, 0x08, 0xd3, 0xa8, 0x52, 0x43, 0xfa, 0xfe, 0x9c, /* |.8....V....RC...| */ +/* 000001b0 */ 0x76, 0x01, 0x61, 0x00, 0x01, 0x98, 0x0c, 0x44, 0x40, 0x00, 0x6e, 0x02, 0x70, 0xff, 0x3f, 0x00, /* |v.a....D@.n.p.?.| */ +/* 000001c0 */ 0x66, 0x04, 0x61, 0x00, 0x01, 0xf0, 0xa8, 0x53, 0x55, 0x4f, 0x42, 0xb8, 0x0a, 0xf2, 0xa9, 0x95, /* |f.a....SUOB.....| */ +/* 000001d0 */ 0x4a, 0x5f, 0x6b, 0x00, 0x01, 0x56, 0x3e, 0x1f, 0x20, 0x5f, 0xa0, 0x57, 0x21, 0xf8, 0x02, 0xa6, /* |J_k..V>. _.W!...| */ +/* 000001e0 */ 0x01, 0x18, 0x59, 0x4f, 0x2f, 0x3c, 0x44, 0x53, 0x41, 0x54, 0x42, 0x67, 0xa9, 0xa0, 0x2a, 0x1f, /* |..YO/ Date: Mon, 2 Oct 2017 23:06:50 +0200 Subject: [PATCH 03/29] LaunchAPPL: modularize; Executor and MiniVMac backends for now --- AutomatedTests/CMakeLists.txt | 25 ++- AutomatedTests/Log.c | 2 - LaunchAPPL/CMakeLists.txt | 9 +- LaunchAPPL/Executor.cc | 48 +++++ LaunchAPPL/Executor.h | 16 ++ LaunchAPPL/LaunchAPPL.cc | 324 ++++------------------------------ LaunchAPPL/LaunchMethod.cc | 20 +++ LaunchAPPL/LaunchMethod.h | 28 +++ LaunchAPPL/Launcher.cc | 132 ++++++++++++++ LaunchAPPL/Launcher.h | 31 ++++ LaunchAPPL/MiniVMac.cc | 195 ++++++++++++++++++++ LaunchAPPL/MiniVMac.h | 16 ++ LaunchAPPL/bootblock | Bin 0 -> 1024 bytes 13 files changed, 547 insertions(+), 299 deletions(-) create mode 100644 LaunchAPPL/Executor.cc create mode 100644 LaunchAPPL/Executor.h create mode 100644 LaunchAPPL/LaunchMethod.cc create mode 100644 LaunchAPPL/LaunchMethod.h create mode 100644 LaunchAPPL/Launcher.cc create mode 100644 LaunchAPPL/Launcher.h create mode 100644 LaunchAPPL/MiniVMac.cc create mode 100644 LaunchAPPL/MiniVMac.h create mode 100644 LaunchAPPL/bootblock diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 4378cddb18..f50aeba184 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -1,20 +1,27 @@ enable_testing() +set(RETRO68_TEST_CONFIG "-eminivmac" "-t4" "--minivmac-dir=/home/wolfgang/Emulators" + "--system-image=/home/wolfgang/Emulators/baresystem.dsk" + "--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk") + + add_application(ReallyEmpty ReallyEmpty.c) -add_test(NAME ReallyEmpty COMMAND LaunchAPPL -e ReallyEmpty.bin) +if(CMAKE_SYSTEM_NAME MATCHES Retro68) + set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single") +endif() +add_test(NAME ReallyEmpty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} ReallyEmpty.bin) add_application(Empty Empty.c) -add_test(NAME Empty COMMAND LaunchAPPL -e Empty.bin) +add_test(NAME Empty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Empty.bin) add_application(File File.c) add_test(NAME File - COMMAND LaunchAPPL -e File.bin --logfile=out -t5 + COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} File.bin ) set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") -add_application(Timeout Timeout.c) -add_test(NAME Timeout - COMMAND sh -c "! LaunchAPPL -e Timeout.bin --logfile=out -t2" - ) -set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One;Two" - FAIL_REGULAR_EXPRESSION "Three") + add_application(Log Log.c) + add_test(NAME Log + COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Log.bin + ) + set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") diff --git a/AutomatedTests/Log.c b/AutomatedTests/Log.c index ec6624054c..8c216dde56 100644 --- a/AutomatedTests/Log.c +++ b/AutomatedTests/Log.c @@ -4,8 +4,6 @@ int main() { TEST_LOG_SIZED("One",3); TEST_LOG_SIZED("Two",3); - for(;;) - ; TEST_LOG_SIZED("Three",5); return 0; } diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 14f268d3fd..4dfa3d1e50 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -1,6 +1,13 @@ find_package(Boost COMPONENTS filesystem program_options) -add_executable(LaunchAPPL LaunchAPPL.cc bootblock.c) +add_executable(LaunchAPPL + LaunchAPPL.cc bootblock.c + + LaunchMethod.h LaunchMethod.cc + Launcher.h Launcher.cc + + Executor.h Executor.cc + MiniVMac.h MiniVMac.cc) target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) diff --git a/LaunchAPPL/Executor.cc b/LaunchAPPL/Executor.cc new file mode 100644 index 0000000000..8c9b7d4cc2 --- /dev/null +++ b/LaunchAPPL/Executor.cc @@ -0,0 +1,48 @@ +#include "Executor.h" +#include "Launcher.h" + +namespace po = boost::program_options; + +class ExecutorLauncher : public Launcher +{ +public: + ExecutorLauncher(po::variables_map& options); + virtual ~ExecutorLauncher(); + + virtual bool Go(int timeout = 0); + +}; + +ExecutorLauncher::ExecutorLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::percent_appledouble) +{ + +} + +ExecutorLauncher::~ExecutorLauncher() +{ + +} + +bool ExecutorLauncher::Go(int timeout) +{ + return ChildProcess(options["executor-path"].as(), { appPath.string() }, timeout) == 0; +} + + +void Executor::GetOptions(options_description &desc) +{ + desc.add_options() + ("executor-path", po::value()->default_value("executor"),"path to executor") + ; +} + +bool Executor::CheckOptions(variables_map &options) +{ + return options.count("executor-path") != 0; +} + +std::unique_ptr Executor::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new ExecutorLauncher(options)); +} diff --git a/LaunchAPPL/Executor.h b/LaunchAPPL/Executor.h new file mode 100644 index 0000000000..57dc584416 --- /dev/null +++ b/LaunchAPPL/Executor.h @@ -0,0 +1,16 @@ +#ifndef EXECUTOR_H +#define EXECUTOR_H + +#include "LaunchMethod.h" + +class Executor : public LaunchMethod +{ +public: + virtual std::string GetName() { return "executor"; } + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // EXECUTOR_H diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index e9288c78b7..4befeda29d 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -5,18 +5,11 @@ #include #include -#include -#include -#include - - -#include "ResourceFork.h" -#include "ResourceFile.h" - -extern "C" { -#include "hfs.h" -} +#include "LaunchMethod.h" +#include "Launcher.h" +#include "Executor.h" +#include "MiniVMac.h" namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -34,105 +27,20 @@ static void usage() } -int ChildProcess(string program, vector args) -{ - std::vector argv; - argv.push_back(program.c_str()); - for(string& s : args) - argv.push_back(s.c_str()); - argv.push_back(NULL); - - int timeout = options.count("timeout") ? options["timeout"].as() : 0; - - pid_t pid = fork(); - if(pid < 0) - { - perror("unable to fork"); - return 1; - } - else if(pid == 0) - { - pid_t worker_pid = timeout ? fork() : 0; - if(worker_pid < 0) - { - perror("unable to fork"); - _exit(1); - } - if(worker_pid == 0) - { - execvp(argv[0], const_cast (argv.data())); - perror("exec failed"); - _exit(1); - } - - pid_t timeout_pid = fork(); - if(timeout_pid < 0) - { - perror("unable to fork"); - _exit(1); - } - if(timeout_pid == 0) - { - sleep(timeout); - _exit(0); - } - int wstatus; - pid_t exited_pid = wait(&wstatus); - if(exited_pid == worker_pid) - { - kill(timeout_pid, SIGKILL); - wait(NULL); - if(!WIFEXITED(wstatus)) - { - return 1; - } - else - { - int exitcode = WEXITSTATUS(wstatus); - _exit(exitcode); - } - } - else - { - kill(worker_pid, SIGKILL); - wait(NULL); - _exit(1); - } - } - else - { - int wstatus; - int result = 0; - do - { - result = waitpid(pid, &wstatus, 0); - } while(result == -1 && errno == EINTR); - - if(!WIFEXITED(wstatus)) - { - return 1; - } - else - { - int exitcode = WEXITSTATUS(wstatus); - return exitcode; - } - } - -} int main(int argc, char *argv[]) { + std::vector methods = { + new Executor(), new MiniVMac() + }; desc.add_options() ("help,h", "show this help message") - ("executor,e", "run using executor") - ("minivmac,m", "run using executor") - - ("executor-path", po::value()->default_value("executor"),"path to executor") - ("minivmac-path", po::value()->default_value("minivmac"),"path to minivmac") - ("minivmac-dir", po::value()->default_value("."),"directory containing vMac.ROM") - ("system-image", po::value(),"path to disk image with system") + ("emulator,e", po::value(), "what emulator/environment to use") + ; + for(LaunchMethod *lm : methods) + lm->GetOptions(desc); + desc.add_options() ("timeout,t", po::value(),"abort after timeout") ("timeout-ok","timeout counts as success") ("logfile", po::value(), "read log file") @@ -142,7 +50,6 @@ int main(int argc, char *argv[]) ("application,a", po::value(), "application" ) ; alldesc.add(desc).add(hidden); - try { auto parsed = po::command_line_parser(argc, argv) @@ -162,198 +69,41 @@ int main(int argc, char *argv[]) po::notify(options); - if(options.count("help") || !options.count("application")) + if(options.count("help") || !options.count("application") || !options.count("emulator")) { usage(); return 0; } - ResourceFile app(options["application"].as()); - if(!app.read()) + LaunchMethod *method = NULL; + for(LaunchMethod *lm : methods) { - std::cerr << "Could not read application file.\n"; + if(lm->GetName() == options["emulator"].as()) + { + method = lm; + break; + } + } + if(!method) + { + std::cerr << "ERROR: unknown emulator/environment.\n"; return 1; } - - - if(options.count("executor")) + if(!method->CheckOptions(options)) { - fs::path tempDir = fs::unique_path(); - fs::create_directories(tempDir); - - fs::path appPath = tempDir / "Application"; - - app.assign(appPath.string(), ResourceFile::Format::percent_appledouble); - if(!app.write()) - { - std::cerr << "Could not write application file.\n"; - return 1; - } - - if(options.count("logfile")) - { - fs::ofstream out(tempDir/options["logfile"].as()); - } - - int result = ChildProcess(options["executor-path"].as(), { appPath.string() }); - - if(options.count("logfile")) - { - fs::ifstream in(tempDir/options["logfile"].as()); - std::cout << in.rdbuf(); - } - - fs::remove_all(tempDir); - - return result; - } - if(options.count("minivmac")) - { - assert(options.count("system-image")); - fs::path tempDir = fs::unique_path(); - fs::path path = tempDir / "image.dsk"; - fs::create_directories(tempDir); - - hfsvol *sysvol = hfs_mount(options["system-image"].as().c_str(), - 0, HFS_MODE_RDONLY); - - int size = 5000*1024; - fs::ofstream(path, std::ios::binary | std::ios::trunc).seekp(size-1).put(0); - hfs_format(path.string().c_str(), 0, 0, "SysAndApp", 0, NULL); - hfsvol *vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDWR); - - hfsvolent ent; - hfs_vstat(sysvol, &ent); - - hfs_setcwd(sysvol, ent.blessed); - - - - - { - const char *fn = "System"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); - hfs_setfork(in, 0); - hfs_setfork(out, 0); - hfs_read(in, buffer.data(), fileent.u.file.dsize); - hfs_write(out, buffer.data(), fileent.u.file.dsize); - hfs_setfork(in, 1); - hfs_setfork(out, 1); - hfs_read(in, buffer.data(), fileent.u.file.rsize); - hfs_write(out, buffer.data(), fileent.u.file.rsize); - hfs_close(in); - hfs_close(out); - } - { - const char *fn = "Finder"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); - hfs_setfork(in, 0); - hfs_setfork(out, 0); - hfs_read(in, buffer.data(), fileent.u.file.dsize); - hfs_write(out, buffer.data(), fileent.u.file.dsize); - hfs_setfork(in, 1); - hfs_setfork(out, 1); - hfs_read(in, buffer.data(), fileent.u.file.rsize); - hfs_write(out, buffer.data(), fileent.u.file.rsize); - hfs_close(in); - hfs_close(out); - } - { - const char *fn = "MacsBug"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); - hfs_setfork(in, 0); - hfs_setfork(out, 0); - hfs_read(in, buffer.data(), fileent.u.file.dsize); - hfs_write(out, buffer.data(), fileent.u.file.dsize); - hfs_setfork(in, 1); - hfs_setfork(out, 1); - hfs_read(in, buffer.data(), fileent.u.file.rsize); - hfs_write(out, buffer.data(), fileent.u.file.rsize); - hfs_close(in); - hfs_close(out); - } - - { - std::ostringstream rsrcOut; - app.resources.writeFork(rsrcOut); - std::string rsrc = rsrcOut.str(); - std::string& data = app.data; - - hfsfile *file = hfs_create(vol, "App","APPL","????"); - hfs_setfork(file, 0); - hfs_write(file, data.data(), data.size()); - hfs_setfork(file, 1); - hfs_write(file, rsrc.data(), rsrc.size()); - hfs_close(file); - } - - { - hfsfile *out = hfs_create(vol, "out", "TEXT", "????"); - hfs_close(out); - } - - hfs_vstat(vol, &ent); - ent.blessed = hfs_getcwd(vol); - std::cout << "blessed: " << ent.blessed << std::endl; - hfs_vsetattr(vol, &ent); - - hfs_umount(vol); - hfs_umount(sysvol); - - extern unsigned char bootblock[1024]; - std::vector bootblock1(bootblock, bootblock+1024); - std::fstream out(path.string(), std::ios::in | std::ios::out | std::ios::binary); - - bootblock1[0x5A] = 3; - bootblock1[0x5B] = 'A'; - bootblock1[0x5C] = 'p'; - bootblock1[0x5D] = 'p'; - - out.write((const char*) bootblock1.data(), 1024); - - path = fs::absolute(path); - - fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); - fs::path minivmacpath = fs::absolute( minivmacdir / options["minivmac-path"].as() ); - - fs::current_path(minivmacdir); - - int result = ChildProcess(minivmacpath.string(), { path.string() }); - - std::cerr << "volume at: " << path.string() << std::endl; - vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDONLY); - { - hfsfile *out = hfs_open(vol, "out"); - if(!out) - return 1; - hfsdirent fileent; - int statres = hfs_stat(vol, "out", &fileent); - std::cerr << "stat: " << statres << "\n"; - std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; - std::vector buffer(fileent.u.file.dsize); - hfs_setfork(out, 0); - hfs_read(out, buffer.data(), fileent.u.file.dsize); - hfs_close(out); - std::cout << string(buffer.begin(), buffer.end()); - } - hfs_umount(vol); + std::cerr << "Missing configuration.\n"; + return 1; } - return 0; + std::unique_ptr launcher = method->MakeLauncher(options); + + int timeout = options.count("timeout") ? options["timeout"].as() : 0; + + bool result = launcher->Go(timeout); + + launcher->DumpOutput(); + + + return result ? 0 : 1; } diff --git a/LaunchAPPL/LaunchMethod.cc b/LaunchAPPL/LaunchMethod.cc new file mode 100644 index 0000000000..34560d31db --- /dev/null +++ b/LaunchAPPL/LaunchMethod.cc @@ -0,0 +1,20 @@ +#include "LaunchMethod.h" + +LaunchMethod::LaunchMethod() +{ + +} + +LaunchMethod::~LaunchMethod() +{ + +} + +void LaunchMethod::GetOptions(boost::program_options::options_description &desc) +{ +} + +bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options) +{ + return true; +} diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h new file mode 100644 index 0000000000..ab208db8e0 --- /dev/null +++ b/LaunchAPPL/LaunchMethod.h @@ -0,0 +1,28 @@ +#ifndef LAUNCHMETHOD_H +#define LAUNCHMETHOD_H + +#include +#include +#include + +#include + +class Launcher; + +class LaunchMethod +{ +public: + typedef boost::program_options::options_description options_description; + typedef boost::program_options::variables_map variables_map; + + LaunchMethod(); + virtual ~LaunchMethod(); + + virtual std::string GetName() = 0; + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options) = 0; +}; + +#endif // LAUNCHMETHOD_H diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc new file mode 100644 index 0000000000..41038f7278 --- /dev/null +++ b/LaunchAPPL/Launcher.cc @@ -0,0 +1,132 @@ +#include "Launcher.h" +#include +#include +#include +#include +#include + + +namespace fs = boost::filesystem; +using std::string; +using std::vector; + +Launcher::Launcher(boost::program_options::variables_map &options) + : options(options) +{ + app.assign(options["application"].as()); + if(!app.read()) + throw std::runtime_error("Could not load application file."); + + tempDir = fs::absolute(fs::unique_path()); + fs::create_directories(tempDir); + + appPath = tempDir / "Application"; + outPath = tempDir / "out"; + + fs::ofstream out(outPath); +} + +Launcher::Launcher(boost::program_options::variables_map &options, ResourceFile::Format f) + : Launcher(options) +{ + app.assign(appPath.string(), f); + app.write(); +} + +void Launcher::DumpOutput() +{ + fs::ifstream in(outPath); + std::cout << in.rdbuf(); +} + +Launcher::~Launcher() +{ + fs::remove_all(tempDir); +} + + + +int Launcher::ChildProcess(string program, vector args, int timeout) +{ + std::vector argv; + argv.push_back(program.c_str()); + for(string& s : args) + argv.push_back(s.c_str()); + argv.push_back(NULL); + + pid_t pid = fork(); + if(pid < 0) + { + perror("unable to fork"); + return 1; + } + else if(pid == 0) + { + pid_t worker_pid = timeout ? fork() : 0; + if(worker_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(worker_pid == 0) + { + execvp(argv[0], const_cast (argv.data())); + perror("exec failed"); + _exit(1); + } + + pid_t timeout_pid = fork(); + if(timeout_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(timeout_pid == 0) + { + sleep(timeout); + _exit(0); + } + int wstatus; + pid_t exited_pid = wait(&wstatus); + if(exited_pid == worker_pid) + { + kill(timeout_pid, SIGKILL); + wait(NULL); + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + _exit(exitcode); + } + } + else + { + kill(worker_pid, SIGKILL); + wait(NULL); + _exit(1); + } + } + else + { + int wstatus; + int result = 0; + do + { + result = waitpid(pid, &wstatus, 0); + } while(result == -1 && errno == EINTR); + + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + return exitcode; + } + } + +} diff --git a/LaunchAPPL/Launcher.h b/LaunchAPPL/Launcher.h new file mode 100644 index 0000000000..e6bf703725 --- /dev/null +++ b/LaunchAPPL/Launcher.h @@ -0,0 +1,31 @@ +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#include +#include "ResourceFile.h" +#include + +class Launcher +{ +protected: + boost::program_options::variables_map& options; + + ResourceFile app; + boost::filesystem::path tempDir, appPath, outPath; + bool keepTempFiles; + + int ChildProcess(std::string program, std::vector args, int timeout); +public: + Launcher(boost::program_options::variables_map& options); + Launcher(boost::program_options::variables_map& options, ResourceFile::Format f); + + void KeepTempFiles() { keepTempFiles = true; } + + virtual bool Go(int timeout = 0) = 0; + + virtual void DumpOutput(); + + virtual ~Launcher(); +}; + +#endif // LAUNCHER_H diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc new file mode 100644 index 0000000000..4f8f686b67 --- /dev/null +++ b/LaunchAPPL/MiniVMac.cc @@ -0,0 +1,195 @@ +#include "MiniVMac.h" +#include "Launcher.h" + +extern "C" { +#include "hfs.h" +} + +#include + +namespace fs = boost::filesystem; +using std::string; +using std::vector; + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +class MiniVMacLauncher : public Launcher +{ + fs::path imagePath; + fs::path systemImage; + + hfsvol *sysvol; + hfsvol *vol; + + void CopySystemFile(const char* fn, const char *dstfn = NULL); +public: + MiniVMacLauncher(po::variables_map& options); + virtual ~MiniVMacLauncher(); + + virtual bool Go(int timeout = 0); + virtual void DumpOutput(); +}; + + +MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::percent_appledouble), + sysvol(NULL), vol(NULL) +{ + imagePath = fs::absolute(tempDir / "image.dsk"); + systemImage = fs::absolute(options["system-image"].as()); + fs::path autoquitImage = fs::absolute(options["autoquit-image"].as()); + + sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY); + assert(sysvol); + hfsvolent ent; + hfs_vstat(sysvol, &ent); + hfs_setcwd(sysvol, ent.blessed); + + + int size = 5000*1024; + + fs::ofstream(imagePath, std::ios::binary | std::ios::trunc).seekp(size-1).put(0); + hfs_format(imagePath.string().c_str(), 0, 0, "SysAndApp", 0, NULL); + + { + extern unsigned char bootblock[1024]; + std::vector bootblock1(bootblock, bootblock+1024); + + bootblock1[0x1A] = 8; + memcpy(&bootblock1[0x1B],"AutoQuit", 8); + bootblock1[0x5A] = 3; + memcpy(&bootblock1[0x5B],"App", 3); + + std::fstream(imagePath.string(), std::ios::in | std::ios::out | std::ios::binary) + .write((const char*) bootblock1.data(), 1024); + } + + + vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDWR); + assert(vol); + + hfs_vstat(vol, &ent); + ent.blessed = hfs_getcwd(vol); + hfs_vsetattr(vol, &ent); + + + + CopySystemFile("System"); + CopySystemFile("Finder"); + CopySystemFile("MacsBug"); + + { + std::ostringstream rsrcOut; + app.resources.writeFork(rsrcOut); + std::string rsrc = rsrcOut.str(); + std::string& data = app.data; + + hfsfile *file = hfs_create(vol, "App","APPL","????"); + hfs_setfork(file, 0); + hfs_write(file, data.data(), data.size()); + hfs_setfork(file, 1); + hfs_write(file, rsrc.data(), rsrc.size()); + hfs_close(file); + } + + hfs_umount(sysvol); + sysvol = hfs_mount(autoquitImage.string().c_str(),0, HFS_MODE_RDONLY); + assert(sysvol); + CopySystemFile("AutoQuit"); + + { + hfsfile *file = hfs_create(vol, "out", "TEXT", "MPS "); + hfs_close(file); + } + + hfs_umount(sysvol); sysvol = NULL; + hfs_umount(vol); vol = NULL; +} + +MiniVMacLauncher::~MiniVMacLauncher() +{ + if(sysvol) + hfs_umount(sysvol); + if(vol) + hfs_umount(vol); + +} + +void MiniVMacLauncher::CopySystemFile(const char *fn, const char *dstfn) +{ + if(!dstfn) + dstfn = fn; + hfsdirent fileent; + if(hfs_stat(sysvol, fn, &fileent) < 0) + return; + hfsfile *in = hfs_open(sysvol, fn); + hfsfile *out = hfs_create(vol, dstfn, fileent.u.file.type,fileent.u.file.creator); + + std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); + hfs_setfork(in, 0); + hfs_setfork(out, 0); + hfs_read(in, buffer.data(), fileent.u.file.dsize); + hfs_write(out, buffer.data(), fileent.u.file.dsize); + hfs_setfork(in, 1); + hfs_setfork(out, 1); + hfs_read(in, buffer.data(), fileent.u.file.rsize); + hfs_write(out, buffer.data(), fileent.u.file.rsize); + hfs_close(in); + hfs_close(out); +} + + +bool MiniVMacLauncher::Go(int timeout) +{ + fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); + std::string minivmacpath = options["minivmac-path"].as(); + + fs::current_path(minivmacdir); + + return ChildProcess(minivmacpath, { imagePath.string() }, timeout) == 0; +} + +void MiniVMacLauncher::DumpOutput() +{ + sleep(1); + vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY); + hfsdirent fileent; + int statres = hfs_stat(vol, "out", &fileent); + std::cerr << "stat: " << statres << "\n"; + std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; + + hfsfile *out = hfs_open(vol, "out"); + if(!out) + return; + std::vector buffer(fileent.u.file.dsize); + hfs_setfork(out, 0); + hfs_read(out, buffer.data(), fileent.u.file.dsize); + hfs_close(out); + std::cout << string(buffer.begin(), buffer.end()); + hfs_umount(vol); vol = NULL; +} + + +void MiniVMac::GetOptions(options_description &desc) +{ + desc.add_options() + ("minivmac-dir", po::value(),"directory containing vMac.ROM") + ("minivmac-path", po::value()->default_value("./minivmac"),"relative path to minivmac") + ("system-image", po::value(),"path to disk image with system") + ("autoquit-image", po::value(),"path to autoquit disk image, available from the minivmac web site") + ; +} + +bool MiniVMac::CheckOptions(variables_map &options) +{ + return options.count("minivmac-path") != 0 + && options.count("minivmac-dir") != 0 + && options.count("system-image") != 0 + && options.count("autoquit-image") != 0; +} + +std::unique_ptr MiniVMac::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new MiniVMacLauncher(options)); +} diff --git a/LaunchAPPL/MiniVMac.h b/LaunchAPPL/MiniVMac.h new file mode 100644 index 0000000000..0f83aa4ad8 --- /dev/null +++ b/LaunchAPPL/MiniVMac.h @@ -0,0 +1,16 @@ +#ifndef MINIVMAC_H +#define MINIVMAC_H + +#include "LaunchMethod.h" + +class MiniVMac : public LaunchMethod +{ +public: + virtual std::string GetName() { return "minivmac"; } + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // MINIVMAC_H diff --git a/LaunchAPPL/bootblock b/LaunchAPPL/bootblock new file mode 100644 index 0000000000000000000000000000000000000000..40d64c68624795751214ae4337787deefed9f2c3 GIT binary patch literal 1024 zcmZ`&PfXip6o3Bkf|GiXb%<6R!j~+X+6j@6^?<5qwH*j1Fq;x!*{LxlwxIm+oCKv( zG;PzA;{=;Fp=z33Z7TTEL)Ak%O_N)3AUEhPRS(lvb=3}?hN^B#(-7}-f#W_){(j%j z?|tvR-+LY(%K}v36o4mvHDAar;p62=&M(j9jN>D2e_y$rpDk*~1UodJSMvGX((K|Z ztJl*7#VAbc>B~kgw~Xop-M3-S)i@Yz7i_YAP+nJ`=8!M|q1$W~Onh=XOMI~PEu$$5V(JuM4W-!tp$ z)TJvx&Aiu(FPR?%>*xk``a?MqA4>C6f@YcT^lCr_@NjS!{kYZA&k)~k94Ebp*!hE& z=3Ah3>QQE$=t@@sXrNdUlOa}N72vtFuK%k5JSLnQ)vq#y`G+v~-8xnsf?C9mme$?K z=$6iIhJri3r)HliM_#kOFN2%Qs5Clt!DE^Ir!+Da)|Yq$Pe^O-I~tchirP_oGRYo?$v`e?;_tg_}SHo>}sNpesi%<^1?{t;2UK@W5_Nkv#L+fgw4naEL+d? zvsTS=GII8&!r-gGk<50F`cQUW!s&rBv_sEPPiVIvQt@NRLhDItL_=0hDteAo$bu74 zw)Huymc6aNDQvov6&Zt*ST;=HV+VEdskO`U;SD-^&0FSG~)>Cpzhg5v{zw1AKi)iEk literal 0 HcmV?d00001 From c01a99637aad5e3440a36e67315ef3d7fe6c3d5a Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Mon, 2 Oct 2017 23:13:07 +0200 Subject: [PATCH 04/29] add a missing error check (one of many) --- ResourceFiles/ResourceFile.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index b516c735c5..cf8ea7a611 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -490,6 +490,8 @@ bool ResourceFile::write() hfs_format(pathstring.c_str(), 0, 0, path.stem().string().substr(0,27).c_str(), 0, NULL); hfsvol *vol = hfs_mount(pathstring.c_str(), 0, HFS_MODE_RDWR); + if(!vol) + return false; //hfs_setvol(vol, ) hfsfile *file = hfs_create(vol, (path.stem().string().substr(0,31)).c_str(), ((std::string)type).c_str(), ((std::string)creator).c_str()); From eb9dd5aaff5a2ed8753121c32e9ff52a72d60c46 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Mon, 2 Oct 2017 23:43:35 +0200 Subject: [PATCH 05/29] AutomatedTests: clean up CMakeLists.txt --- AutomatedTests/CMakeLists.txt | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index f50aeba184..2bddc976ed 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -4,24 +4,23 @@ set(RETRO68_TEST_CONFIG "-eminivmac" "-t4" "--minivmac-dir=/home/wolfgang/Emulat "--system-image=/home/wolfgang/Emulators/baresystem.dsk" "--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk") +find_program(LAUNCH_APPL LaunchAPPL PATH "${CMAKE_INSTALL_PREFIX}/../bin/") -add_application(ReallyEmpty ReallyEmpty.c) +function(test name) + add_application(${name} ${name}.c Test.h Test.c) + add_test(NAME ${name} COMMAND ${LAUNCH_APPL} ${RETRO68_TEST_CONFIG} ${name}.bin) +endfunction() + + +test(ReallyEmpty) if(CMAKE_SYSTEM_NAME MATCHES Retro68) set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single") endif() -add_test(NAME ReallyEmpty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} ReallyEmpty.bin) -add_application(Empty Empty.c) -add_test(NAME Empty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Empty.bin) +test(Empty) -add_application(File File.c) -add_test(NAME File - COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} File.bin - ) +test(File) set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") - add_application(Log Log.c) - add_test(NAME Log - COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Log.bin - ) - set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") +test(Log) +set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") From 5f998b85be6bfcaa2df4164b8cd89c0e0ca1abe7 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 00:17:31 +0200 Subject: [PATCH 06/29] ResourceFiles: read data forks from MacBinary and AppleSingle --- ResourceFiles/ResourceFile.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index cf8ea7a611..4d40db699c 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -265,13 +265,14 @@ bool ResourceFile::read() in.seekg(26 + i * 12); int what = longword(in); int off = longword(in); - //int len = longword(in); + int len = longword(in); in.seekg(off); switch(what) { case 1: - // ### - // FIXME: read data fork + std::vector buf(len); + in.read(buf, len); + data = std::string(buf.begin(), buf.end()); break; case 2: resources = Resources(in); @@ -342,7 +343,9 @@ bool ResourceFile::read() unsigned short crc = CalculateCRC(0,header,124); if(word(in) != crc) return false; - // FIXME: read data fork + std::vector buf(datasize); + in.read(buf, datasize); + data = std::string(buf.begin(), buf.end()); in.seekg(128 + datasize); resources = Resources(in); } From 2885f5d8cd2781293c02466c815719716bb614df Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 11:57:56 +0200 Subject: [PATCH 07/29] LaunchAPPL: Classic and Carbon methods --- AutomatedTests/CMakeLists.txt | 28 ++++++++++----- AutomatedTests/Init.cc | 22 ++++++++++++ AutomatedTests/Test.h | 5 ++- LaunchAPPL/CMakeLists.txt | 22 ++++++++++-- LaunchAPPL/Carbon.cc | 35 ++++++++++++++++++ LaunchAPPL/Carbon.h | 14 ++++++++ LaunchAPPL/Classic.cc | 68 +++++++++++++++++++++++++++++++++++ LaunchAPPL/Classic.h | 14 ++++++++ LaunchAPPL/LaunchAPPL.cc | 7 ++++ LaunchAPPL/MiniVMac.cc | 2 ++ 10 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 AutomatedTests/Init.cc create mode 100644 LaunchAPPL/Carbon.cc create mode 100644 LaunchAPPL/Carbon.h create mode 100644 LaunchAPPL/Classic.cc create mode 100644 LaunchAPPL/Classic.h diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 2bddc976ed..3cfb851fca 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -1,26 +1,36 @@ enable_testing() -set(RETRO68_TEST_CONFIG "-eminivmac" "-t4" "--minivmac-dir=/home/wolfgang/Emulators" +set(RETRO68_LAUNCH_METHOD classic CACHE String "How to launch Mac applications (for automated testing)") + +set(RETRO68_TEST_CONFIG "-t4" "--minivmac-dir=/home/wolfgang/Emulators" "--system-image=/home/wolfgang/Emulators/baresystem.dsk" "--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk") find_program(LAUNCH_APPL LaunchAPPL PATH "${CMAKE_INSTALL_PREFIX}/../bin/") -function(test name) - add_application(${name} ${name}.c Test.h Test.c) - add_test(NAME ${name} COMMAND ${LAUNCH_APPL} ${RETRO68_TEST_CONFIG} ${name}.bin) +function(test FILE) + get_filename_component(NAME ${FILE} NAME_WE) + add_application(${NAME} ${FILE} Test.h Test.c) + add_test(NAME ${NAME} COMMAND ${LAUNCH_APPL} + -e ${RETRO68_LAUNCH_METHOD} ${RETRO68_TEST_CONFIG} ${ARGN} ${NAME}.bin) endfunction() - -test(ReallyEmpty) if(CMAKE_SYSTEM_NAME MATCHES Retro68) + test(ReallyEmpty.c) set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single") endif() -test(Empty) +test(Empty.c) -test(File) +test(File.c) set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") -test(Log) +test(Timeout.c) +set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo") + +test(Log.c) set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") + +test(Init.cc) +set_tests_properties(Init PROPERTIES PASS_REGULAR_EXPRESSION "constructor\nmain\ndestructor") + diff --git a/AutomatedTests/Init.cc b/AutomatedTests/Init.cc new file mode 100644 index 0000000000..1ec84912f7 --- /dev/null +++ b/AutomatedTests/Init.cc @@ -0,0 +1,22 @@ +#include "Test.h" + +class Constructed +{ +public: + Constructed() + { + TestLog("constructor"); + } + ~Constructed() + { + TestLog("destructor"); + } +}; + +Constructed thing; + +int main() +{ + TestLog("main"); + return 0; +} diff --git a/AutomatedTests/Test.h b/AutomatedTests/Test.h index 46c3b66940..b43e622931 100644 --- a/AutomatedTests/Test.h +++ b/AutomatedTests/Test.h @@ -26,7 +26,7 @@ _ref = _hpb.ioParam.ioRefNum; \ \ memset(&_hpb,0,sizeof(_hpb)); \ - _hpb.ioParam.ioBuffer = str; \ + _hpb.ioParam.ioBuffer = (Ptr)str; \ _hpb.ioParam.ioReqCount = size; \ _hpb.ioParam.ioPosMode = fsFromLEOF; \ _hpb.ioParam.ioPosOffset = 0; \ @@ -46,6 +46,9 @@ FlushVol(NULL,0); \ } while(0); +#ifdef __cplusplus +extern "C" +#endif void TestLog(const char *str); #endif // TEST_H diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 4dfa3d1e50..c5be453b42 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -1,14 +1,32 @@ find_package(Boost COMPONENTS filesystem program_options) +set(LAUNCHMETHODS + Executor.h Executor.cc + MiniVMac.h MiniVMac.cc +) + +if(APPLE) +LIST(APPEND LAUNCHMETHODS + Classic.h Classic.cc + Carbon.h Carbon.cc + ) +endif() + add_executable(LaunchAPPL LaunchAPPL.cc bootblock.c LaunchMethod.h LaunchMethod.cc Launcher.h Launcher.cc - Executor.h Executor.cc - MiniVMac.h MiniVMac.cc) + ${LAUNCHMETHODS}) + + target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) +if(APPLE) + find_library(APPLICATIONSERVICES_FW ApplicationServices) + target_link_libraries(LaunchAPPL ${APPLICATIONSERVICES_FW}) +endif() + install(TARGETS LaunchAPPL RUNTIME DESTINATION bin) diff --git a/LaunchAPPL/Carbon.cc b/LaunchAPPL/Carbon.cc new file mode 100644 index 0000000000..22ef6e92e6 --- /dev/null +++ b/LaunchAPPL/Carbon.cc @@ -0,0 +1,35 @@ +#include "Carbon.h" +#include "Launcher.h" + +namespace po = boost::program_options; + +class CarbonLauncher : public Launcher +{ +public: + CarbonLauncher(po::variables_map& options); + virtual ~CarbonLauncher(); + + virtual bool Go(int timeout = 0); + +}; + +CarbonLauncher::CarbonLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::real) +{ + +} + +CarbonLauncher::~CarbonLauncher() +{ + +} + +bool CarbonLauncher::Go(int timeout) +{ + return ChildProcess("LaunchCarbon", { appPath.string() }, timeout) == 0; +} + +std::unique_ptr Carbon::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new CarbonLauncher(options)); +} diff --git a/LaunchAPPL/Carbon.h b/LaunchAPPL/Carbon.h new file mode 100644 index 0000000000..f9c2ce219e --- /dev/null +++ b/LaunchAPPL/Carbon.h @@ -0,0 +1,14 @@ +#ifndef CARBON_METHOD_H +#define CARBON_METHOD_H + +#include "LaunchMethod.h" + +class Carbon : public LaunchMethod +{ +public: + virtual std::string GetName() { return "carbon"; } + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // CARBON_METHOD_H diff --git a/LaunchAPPL/Classic.cc b/LaunchAPPL/Classic.cc new file mode 100644 index 0000000000..45fdceb630 --- /dev/null +++ b/LaunchAPPL/Classic.cc @@ -0,0 +1,68 @@ +#include "Classic.h" +#include "Launcher.h" + +#define ResType MacResType +#include + +namespace po = boost::program_options; + +class ClassicLauncher : public Launcher +{ +public: + ClassicLauncher(po::variables_map& options); + virtual ~ClassicLauncher(); + + virtual bool Go(int timeout = 0); + +}; + +ClassicLauncher::ClassicLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::real) +{ + +} + +ClassicLauncher::~ClassicLauncher() +{ + +} + +bool ClassicLauncher::Go(int timeout) +{ + FSRef ref; + FSPathMakeRef((const UInt8*) appPath.string().c_str(), &ref, NULL); + LSApplicationParameters params; + memset(¶ms, 0, sizeof(params)); + params.flags = kLSLaunchStartClassic + | kLSLaunchInClassic + | kLSLaunchDontAddToRecents + | kLSLaunchNewInstance; + params.application = &ref; + + ProcessSerialNumber psn; + LSOpenApplication(¶ms, &psn); + + // Classic startup takes place before LSOpenApplication returns, + // so no extra timeout is needed + + for(int i = 0; i < timeout || timeout == 0; i++) + { + sleep(1); + + ProcessInfoRec pi; + pi.processInfoLength = sizeof(pi); + pi.processName = NULL; + pi.processAppSpec = 0; + if(GetProcessInformation(&psn, &pi) == procNotFound) + return true; + } + + KillProcess(&psn); + + return false; +} + +std::unique_ptr Classic::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new ClassicLauncher(options)); +} diff --git a/LaunchAPPL/Classic.h b/LaunchAPPL/Classic.h new file mode 100644 index 0000000000..17b5627603 --- /dev/null +++ b/LaunchAPPL/Classic.h @@ -0,0 +1,14 @@ +#ifndef CLASSIC_H +#define CLASSIC_H + +#include "LaunchMethod.h" + +class Classic : public LaunchMethod +{ +public: + virtual std::string GetName() { return "classic"; } + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // CLASSIC_H diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 4befeda29d..89b4b5935a 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -8,6 +8,10 @@ #include "LaunchMethod.h" #include "Launcher.h" +#ifdef __APPLE__ +#include "Classic.h" +#include "Carbon.h" +#endif #include "Executor.h" #include "MiniVMac.h" @@ -31,6 +35,9 @@ static void usage() int main(int argc, char *argv[]) { std::vector methods = { +#ifdef __APPLE__ + new Classic(), new Carbon(), +#endif new Executor(), new MiniVMac() }; desc.add_options() diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 4f8f686b67..5b78bef100 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -6,6 +6,8 @@ extern "C" { } #include +#include +#include namespace fs = boost::filesystem; using std::string; From 62831a7391e089aa73c91a95dba3cb87566e429e Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 12:08:54 +0200 Subject: [PATCH 08/29] fix ResourceFile data fork reading --- ResourceFiles/ResInfo.cc | 14 +++++++++----- ResourceFiles/ResourceFile.cc | 10 ++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ResourceFiles/ResInfo.cc b/ResourceFiles/ResInfo.cc index ff8c207ecd..6bc01bb72e 100644 --- a/ResourceFiles/ResInfo.cc +++ b/ResourceFiles/ResInfo.cc @@ -38,8 +38,9 @@ int main(int argc, char *argv[]) ("creator,c", "print creator code") ("all,a", "print all info") ("format,f", "print format") - ("count,n", "print number of resources") - ("filename,l", "echo input file name") + ("count,n", "print number of resources") + ("size,s", "show data fork size") + ("filename,l", "echo input file name") ("set-format,F", po::value(), "resource fork format)") ; po::options_description hidden, alldesc; @@ -82,11 +83,12 @@ int main(int argc, char *argv[]) bool showCreator = options.count("creator") != 0; bool showFormat = options.count("format") != 0; bool showCount = options.count("count") != 0; - + bool showSize = options.count("size") != 0; + ResourceFile::Format format = ResourceFile::Format::autodetect; if(options.count("all")) - showType = showCreator = showFormat = showCount = true; + showType = showCreator = showFormat = showCount = showSize = true; if(options.count("set-format")) { @@ -121,7 +123,9 @@ int main(int argc, char *argv[]) out << " " << reverseFormats[rsrcFile.format]; if(showCount) out << " " << rsrcFile.resources.resources.size(); - + if(showSize) + out << " " << rsrcFile.data.size(); + string str = out.str(); if(str.size()) { diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index 4d40db699c..ba9b332ed8 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -270,9 +270,11 @@ bool ResourceFile::read() switch(what) { case 1: - std::vector buf(len); - in.read(buf, len); - data = std::string(buf.begin(), buf.end()); + { + std::vector buf(len); + in.read(buf.data(), len); + data = std::string(buf.begin(), buf.end()); + } break; case 2: resources = Resources(in); @@ -344,7 +346,7 @@ bool ResourceFile::read() if(word(in) != crc) return false; std::vector buf(datasize); - in.read(buf, datasize); + in.read(buf.data(), datasize); data = std::string(buf.begin(), buf.end()); in.seekg(128 + datasize); resources = Resources(in); From 28b11ae71985c4d715bc014375b6509dcd08ddaf Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 13:00:53 +0200 Subject: [PATCH 09/29] LaunchCFM: better conditionalization for Classic/Carbon backends --- LaunchAPPL/CMakeLists.txt | 13 +++++++++---- LaunchAPPL/Classic.cc | 2 ++ LaunchAPPL/LaunchAPPL.cc | 15 ++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index c5be453b42..8d7b177d16 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -6,10 +6,15 @@ set(LAUNCHMETHODS ) if(APPLE) -LIST(APPEND LAUNCHMETHODS - Classic.h Classic.cc - Carbon.h Carbon.cc - ) + LIST(APPEND LAUNCHMETHODS + Classic.h Classic.cc + ) + find_program(LAUNCHCFMAPP LaunchCFMApp) + if(LAUNCHCFMAPP) + add_definitions(-DHAS_LAUNCHCFMAPP) + LIST(APPEND LAUNCHMETHODS + Carbon.h Carbon.cc) + endif() endif() add_executable(LaunchAPPL diff --git a/LaunchAPPL/Classic.cc b/LaunchAPPL/Classic.cc index 45fdceb630..71d5dc49fa 100644 --- a/LaunchAPPL/Classic.cc +++ b/LaunchAPPL/Classic.cc @@ -1,3 +1,4 @@ +#if defined(__APPLE__) && defined(__powerpc) #include "Classic.h" #include "Launcher.h" @@ -66,3 +67,4 @@ std::unique_ptr Classic::MakeLauncher(variables_map &options) { return std::unique_ptr(new ClassicLauncher(options)); } +#endif diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 89b4b5935a..322d3c654d 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -8,9 +8,11 @@ #include "LaunchMethod.h" #include "Launcher.h" -#ifdef __APPLE__ -#include "Classic.h" -#include "Carbon.h" +#if defined(__APPLE__) && defined(__powerpc) +# include "Classic.h" +#endif +#ifdef HAS_LAUNCHCFMAPP +# include "Carbon.h" #endif #include "Executor.h" #include "MiniVMac.h" @@ -35,8 +37,11 @@ static void usage() int main(int argc, char *argv[]) { std::vector methods = { -#ifdef __APPLE__ - new Classic(), new Carbon(), +#if defined(__APPLE__) && defined(__powerpc) + new Classic(), +#endif +#ifdef HAS_LAUNCHCFMAPP + new Carbon(), #endif new Executor(), new MiniVMac() }; From 5fd28f94b00ec6e6435a06d63d9240be1172afbc Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 13:03:35 +0200 Subject: [PATCH 10/29] LaunchAPPL/minivmac: use boot sector from given system disk, duh... --- LaunchAPPL/MiniVMac.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 5b78bef100..ac42ed035d 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -42,12 +42,6 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) systemImage = fs::absolute(options["system-image"].as()); fs::path autoquitImage = fs::absolute(options["autoquit-image"].as()); - sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY); - assert(sysvol); - hfsvolent ent; - hfs_vstat(sysvol, &ent); - hfs_setcwd(sysvol, ent.blessed); - int size = 5000*1024; @@ -55,15 +49,16 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) hfs_format(imagePath.string().c_str(), 0, 0, "SysAndApp", 0, NULL); { - extern unsigned char bootblock[1024]; - std::vector bootblock1(bootblock, bootblock+1024); + std::vector bootblock1(1024); + + fs::ifstream(systemImage).read((char*) bootblock1.data(), 1024); bootblock1[0x1A] = 8; memcpy(&bootblock1[0x1B],"AutoQuit", 8); bootblock1[0x5A] = 3; memcpy(&bootblock1[0x5B],"App", 3); - std::fstream(imagePath.string(), std::ios::in | std::ios::out | std::ios::binary) + fs::fstream(imagePath, std::ios::in | std::ios::out | std::ios::binary) .write((const char*) bootblock1.data(), 1024); } @@ -71,6 +66,13 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDWR); assert(vol); + sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY); + assert(sysvol); + hfsvolent ent; + hfs_vstat(sysvol, &ent); + hfs_setcwd(sysvol, ent.blessed); + + hfs_vstat(vol, &ent); ent.blessed = hfs_getcwd(vol); hfs_vsetattr(vol, &ent); From c3f16cd8733ddccefd0bd1b3e4981f660980137b Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 18:17:16 +0200 Subject: [PATCH 11/29] LaunchAPPL: remove our own copy of the boot sector --- LaunchAPPL/CMakeLists.txt | 2 +- LaunchAPPL/bootblock | Bin 1024 -> 0 bytes LaunchAPPL/bootblock.c | 67 -------------------------------------- 3 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 LaunchAPPL/bootblock delete mode 100644 LaunchAPPL/bootblock.c diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 8d7b177d16..566513fdb2 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -18,7 +18,7 @@ if(APPLE) endif() add_executable(LaunchAPPL - LaunchAPPL.cc bootblock.c + LaunchAPPL.cc LaunchMethod.h LaunchMethod.cc Launcher.h Launcher.cc diff --git a/LaunchAPPL/bootblock b/LaunchAPPL/bootblock deleted file mode 100644 index 40d64c68624795751214ae4337787deefed9f2c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmZ`&PfXip6o3Bkf|GiXb%<6R!j~+X+6j@6^?<5qwH*j1Fq;x!*{LxlwxIm+oCKv( zG;PzA;{=;Fp=z33Z7TTEL)Ak%O_N)3AUEhPRS(lvb=3}?hN^B#(-7}-f#W_){(j%j z?|tvR-+LY(%K}v36o4mvHDAar;p62=&M(j9jN>D2e_y$rpDk*~1UodJSMvGX((K|Z ztJl*7#VAbc>B~kgw~Xop-M3-S)i@Yz7i_YAP+nJ`=8!M|q1$W~Onh=XOMI~PEu$$5V(JuM4W-!tp$ z)TJvx&Aiu(FPR?%>*xk``a?MqA4>C6f@YcT^lCr_@NjS!{kYZA&k)~k94Ebp*!hE& z=3Ah3>QQE$=t@@sXrNdUlOa}N72vtFuK%k5JSLnQ)vq#y`G+v~-8xnsf?C9mme$?K z=$6iIhJri3r)HliM_#kOFN2%Qs5Clt!DE^Ir!+Da)|Yq$Pe^O-I~tchirP_oGRYo?$v`e?;_tg_}SHo>}sNpesi%<^1?{t;2UK@W5_Nkv#L+fgw4naEL+d? zvsTS=GII8&!r-gGk<50F`cQUW!s&rBv_sEPPiVIvQt@NRLhDItL_=0hDteAo$bu74 zw)Huymc6aNDQvov6&Zt*ST;=HV+VEdskO`U;SD-^&0FSG~)>Cpzhg5v{zw1AKi)iEk diff --git a/LaunchAPPL/bootblock.c b/LaunchAPPL/bootblock.c deleted file mode 100644 index fa1a23eb89..0000000000 --- a/LaunchAPPL/bootblock.c +++ /dev/null @@ -1,67 +0,0 @@ -unsigned char bootblock[] = { -/* 00000000 */ 0x4c, 0x4b, 0x60, 0x00, 0x00, 0x86, 0x00, 0x17, 0x00, 0x00, 0x06, 0x53, 0x79, 0x73, 0x74, 0x65, /* |LK`........Syste| */ -/* 00000010 */ 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x06, 0x46, 0x69, 0x6e, 0x64, 0x65, /* |m .Finde| */ -/* 00000020 */ 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x07, 0x4d, 0x61, 0x63, 0x73, 0x62, /* |r .Macsb| */ -/* 00000030 */ 0x75, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0c, 0x44, 0x69, 0x73, 0x61, 0x73, /* |ug .Disas| */ -/* 00000040 */ 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x20, 0x20, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, /* |sembler .Start| */ -/* 00000050 */ 0x55, 0x70, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x20, 0x06, 0x46, 0x69, 0x6e, 0x64, 0x65, /* |UpScreen .Finde| */ -/* 00000060 */ 0x72, 0x00, 0x81, 0x00, 0x00, 0x72, 0x00, 0x00, 0x64, 0x0c, 0x0e, 0x43, 0x6c, 0x69, 0x70, 0x62, /* |r....r..d..Clipb| */ -/* 00000070 */ 0x6f, 0x61, 0x72, 0x64, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, /* |oard File ......| */ -/* 00000080 */ 0x43, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x22, 0x38, 0x01, 0x08, 0x48, 0x41, /* |C........."8..HA| */ -/* 00000090 */ 0x0c, 0x41, 0x00, 0x04, 0x6e, 0x2c, 0x72, 0x00, 0x50, 0xf9, 0x00, 0x01, 0xff, 0xf0, 0x42, 0xb9, /* |.A..n,r.P.....B.| */ -/* 000000a0 */ 0x00, 0x03, 0xff, 0xf0, 0x4a, 0xb9, 0x00, 0x01, 0xff, 0xf0, 0x67, 0x16, 0x70, 0x02, 0x48, 0x40, /* |....J.....g.p.H@| */ -/* 000000b0 */ 0xd1, 0xb8, 0x01, 0x08, 0xd1, 0xb8, 0x08, 0x24, 0xd1, 0xb8, 0x02, 0x66, 0xd1, 0xb8, 0x01, 0x0c, /* |.......$...f....| */ -/* 000000c0 */ 0x72, 0x04, 0x20, 0x78, 0x02, 0xa6, 0x0c, 0x41, 0x00, 0x08, 0x6f, 0x02, 0x72, 0x08, 0xd1, 0xfb, /* |r. x...A..o.r...| */ -/* 000000d0 */ 0x10, 0xae, 0x2f, 0x08, 0x70, 0x07, 0x41, 0xf8, 0x0a, 0xb8, 0x42, 0x98, 0x51, 0xc8, 0xff, 0xfc, /* |../.p.A...B.Q...| */ -/* 000000e0 */ 0x34, 0x3a, 0xff, 0x9a, 0x70, 0x16, 0xc0, 0xc2, 0x22, 0x00, 0xa7, 0x1e, 0x43, 0xf8, 0x01, 0x54, /* |4:..p..."...C..T| */ -/* 000000f0 */ 0x53, 0x42, 0x32, 0x82, 0x42, 0xa1, 0x42, 0xa1, 0x42, 0x61, 0x23, 0x08, 0x46, 0x58, 0x55, 0x41, /* |SB2.B.B.Ba#.FXUA| */ -/* 00000100 */ 0x66, 0xfa, 0x33, 0x3c, 0xff, 0xef, 0x42, 0x78, 0x01, 0x84, 0x72, 0xfc, 0x70, 0x0f, 0x14, 0x38, /* |f.3<..Bx..r.p..8| */ -/* 00000110 */ 0x02, 0x06, 0xc0, 0x02, 0xd0, 0x40, 0x48, 0x40, 0x10, 0x02, 0xe4, 0x48, 0xc0, 0x41, 0x48, 0x40, /* |.....@H@...H.AH@| */ -/* 00000120 */ 0x21, 0xc0, 0x01, 0x8e, 0x70, 0x0f, 0x14, 0x38, 0x02, 0x09, 0xc0, 0x02, 0xe5, 0x48, 0x21, 0xc0, /* |!...p..8.....H!.| */ -/* 00000130 */ 0x02, 0xf4, 0x10, 0x02, 0xe4, 0x48, 0xc0, 0x41, 0x21, 0xc0, 0x02, 0xf0, 0x41, 0xf8, 0x03, 0x40, /* |.....H.A!...A..@| */ -/* 00000140 */ 0x72, 0x50, 0x42, 0x58, 0x51, 0xc9, 0xff, 0xfc, 0x70, 0x1e, 0xc0, 0xfa, 0xff, 0x2e, 0x32, 0x38, /* |rPBXQ...p.....28| */ -/* 00000150 */ 0x01, 0x08, 0xe2, 0x49, 0xc0, 0xc1, 0x54, 0x40, 0x32, 0x00, 0xa7, 0x1e, 0x21, 0xc8, 0x03, 0x4e, /* |...I..T@2...!..N| */ -/* 00000160 */ 0x30, 0xc1, 0x31, 0xfc, 0x00, 0x02, 0x03, 0x4c, 0x9e, 0xfc, 0x00, 0x32, 0x20, 0x4f, 0x31, 0x78, /* |0.1....L...2 O1x| */ -/* 00000170 */ 0x02, 0x10, 0x00, 0x16, 0xa0, 0x0f, 0x66, 0x00, 0x01, 0xb2, 0xde, 0xfc, 0x00, 0x32, 0x43, 0xf8, /* |......f......2C.| */ -/* 00000180 */ 0x0a, 0xd8, 0x41, 0xfa, 0xfe, 0x86, 0x70, 0x10, 0xa0, 0x2e, 0x55, 0x4f, 0x2f, 0x0f, 0x48, 0x78, /* |..A...p...UO/.Hx| */ -/* 00000190 */ 0x09, 0xfa, 0x20, 0x78, 0x08, 0x10, 0x4e, 0x90, 0x30, 0x1f, 0xe6, 0x48, 0x31, 0xc0, 0x01, 0x06, /* |.. x..N.0..H1...| */ -/* 000001a0 */ 0x08, 0x38, 0x00, 0x06, 0x02, 0x0b, 0x56, 0xf8, 0x08, 0xd3, 0xa8, 0x52, 0x43, 0xfa, 0xfe, 0x9c, /* |.8....V....RC...| */ -/* 000001b0 */ 0x76, 0x01, 0x61, 0x00, 0x01, 0x98, 0x0c, 0x44, 0x40, 0x00, 0x6e, 0x02, 0x70, 0xff, 0x3f, 0x00, /* |v.a....D@.n.p.?.| */ -/* 000001c0 */ 0x66, 0x04, 0x61, 0x00, 0x01, 0xf0, 0xa8, 0x53, 0x55, 0x4f, 0x42, 0xb8, 0x0a, 0xf2, 0xa9, 0x95, /* |f.a....SUOB.....| */ -/* 000001d0 */ 0x4a, 0x5f, 0x6b, 0x00, 0x01, 0x56, 0x3e, 0x1f, 0x20, 0x5f, 0xa0, 0x57, 0x21, 0xf8, 0x02, 0xa6, /* |J_k..V>. _.W!...| */ -/* 000001e0 */ 0x01, 0x18, 0x59, 0x4f, 0x2f, 0x3c, 0x44, 0x53, 0x41, 0x54, 0x42, 0x67, 0xa9, 0xa0, 0x2a, 0x1f, /* |..YO/ Date: Tue, 3 Oct 2017 18:44:06 +0200 Subject: [PATCH 12/29] LaunchAPPL: more cleanup, config file support --- LaunchAPPL/CMakeLists.txt | 1 + LaunchAPPL/Executor.cc | 18 ++++- LaunchAPPL/LaunchAPPL.cc | 110 +++++++++++++++++++++++++----- LaunchAPPL/LaunchAPPL.cfg.example | 69 +++++++++++++++++++ LaunchAPPL/LaunchMethod.cc | 40 +++++++++++ LaunchAPPL/LaunchMethod.h | 3 + LaunchAPPL/MiniVMac.cc | 50 ++++++++------ 7 files changed, 252 insertions(+), 39 deletions(-) create mode 100644 LaunchAPPL/LaunchAPPL.cfg.example diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 566513fdb2..30e846322d 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -16,6 +16,7 @@ if(APPLE) Carbon.h Carbon.cc) endif() endif() +add_definitions(-DRETRO68_PREFIX="${CMAKE_INSTALL_PREFIX}") add_executable(LaunchAPPL LaunchAPPL.cc diff --git a/LaunchAPPL/Executor.cc b/LaunchAPPL/Executor.cc index 8c9b7d4cc2..42277e587c 100644 --- a/LaunchAPPL/Executor.cc +++ b/LaunchAPPL/Executor.cc @@ -26,7 +26,15 @@ ExecutorLauncher::~ExecutorLauncher() bool ExecutorLauncher::Go(int timeout) { - return ChildProcess(options["executor-path"].as(), { appPath.string() }, timeout) == 0; + std::vector args; + if(options.count("executor-option")) + args = options["executor-option"].as>(); + args.push_back(appPath.string()); + + if(options.count("executor-system-folder")) + setenv("SystemFolder", options["executor-system-folder"].as().c_str(), true); + + return ChildProcess(options["executor-path"].as(), args, timeout) == 0; } @@ -34,12 +42,18 @@ void Executor::GetOptions(options_description &desc) { desc.add_options() ("executor-path", po::value()->default_value("executor"),"path to executor") + ("executor-system-folder", po::value(), + "system folder for executor (overrides SystemFolder environment variable)") + ("executor-option", po::value>(), + "pass an option to executor") ; } bool Executor::CheckOptions(variables_map &options) { - return options.count("executor-path") != 0; + if(options.count("executor-path") == 0) + return false; + return CheckExecutable(options["executor-path"].as()); } std::unique_ptr Executor::MakeLauncher(variables_map &options) diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 322d3c654d..38f391b9d6 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -24,38 +24,95 @@ using std::string; using std::vector; static po::options_description desc; -po::variables_map options; +static po::variables_map options; +static vector configFiles; +static vector launchMethods; + +static void RegisterLaunchMethods() +{ + launchMethods = { +#if defined(__APPLE__) && defined(__powerpc) + new Classic(), +#endif +#ifdef HAS_LAUNCHCFMAPP + new Carbon(), +#endif + new Executor(), new MiniVMac() + // #### Add new `LaunchMethod`s here. + }; +} static void usage() { std::cerr << "Usage: " << "LaunchAPPL [options] appl-file\n"; std::cerr << desc << std::endl; + std::cerr << "Defaults are read from:\n"; + for(string str : configFiles) + { + std::cerr << "\t" << str; + if(!std::ifstream(str)) + std::cerr << " (not found)"; + std::cerr << std::endl; + } + std::cerr << std::endl; + + vector configuredMethods, unconfiguredMethods; + for(LaunchMethod *method : launchMethods) + (method->CheckOptions(options) ? configuredMethods : unconfiguredMethods) + .push_back(method->GetName()); + + if(!configuredMethods.empty()) + { + std::cerr << "Available emulators/environments:\n"; + for(string m : configuredMethods) + std::cerr << "\t" << m << std::endl; + } + if(!unconfiguredMethods.empty()) + { + std::cerr << "Emulators/environments needing more configuration:\n"; + for(string m : unconfiguredMethods) + std::cerr << "\t" << m << std::endl; + } + + if(options.count("emulator")) + { + string e = options["emulator"].as(); + std::cerr << "\nChosen emulator/environment: " << e; + if(std::find(configuredMethods.begin(), configuredMethods.end(), e) + != configuredMethods.end()) + std::cerr << "\n"; + else if(std::find(unconfiguredMethods.begin(), unconfiguredMethods.end(), e) + != unconfiguredMethods.end()) + std::cerr << " (needs more configuration)\n"; + else + std::cerr << " (UNKNOWN)\n"; + } + else + { + std::cerr << "\nNo emulator/environment chosen (-e)\n"; + } } int main(int argc, char *argv[]) { - std::vector methods = { -#if defined(__APPLE__) && defined(__powerpc) - new Classic(), -#endif -#ifdef HAS_LAUNCHCFMAPP - new Carbon(), -#endif - new Executor(), new MiniVMac() - }; + RegisterLaunchMethods(); + configFiles = { string(getenv("HOME")) + "/.LaunchAPPL.cfg", RETRO68_PREFIX "/LaunchAPPL.cfg"}; + desc.add_options() ("help,h", "show this help message") + ; + po::options_description configdesc; + configdesc.add_options() ("emulator,e", po::value(), "what emulator/environment to use") ; - for(LaunchMethod *lm : methods) - lm->GetOptions(desc); + for(LaunchMethod *lm : launchMethods) + lm->GetOptions(configdesc); + desc.add(configdesc); desc.add_options() ("timeout,t", po::value(),"abort after timeout") - ("timeout-ok","timeout counts as success") - ("logfile", po::value(), "read log file") ; po::options_description hidden, alldesc; hidden.add_options() @@ -79,6 +136,26 @@ int main(int argc, char *argv[]) return 1; } + for(string configFileName : configFiles) + { + try + { + std::ifstream cfg(configFileName); + if(cfg) + { + auto parsed = po::parse_config_file(cfg,configdesc,false); + + po::store(parsed, options); + } + } + catch(po::error& e) + { + std::cerr << "CONFIG FILE ERROR: " << e.what() << std::endl << std::endl; + usage(); + return 1; + } + } + po::notify(options); if(options.count("help") || !options.count("application") || !options.count("emulator")) @@ -88,7 +165,7 @@ int main(int argc, char *argv[]) } LaunchMethod *method = NULL; - for(LaunchMethod *lm : methods) + for(LaunchMethod *lm : launchMethods) { if(lm->GetName() == options["emulator"].as()) { @@ -98,13 +175,14 @@ int main(int argc, char *argv[]) } if(!method) { - std::cerr << "ERROR: unknown emulator/environment.\n"; + std::cerr << "ERROR: unknown emulator/environment: " << options["emulator"].as() << "\n"; return 1; } if(!method->CheckOptions(options)) { std::cerr << "Missing configuration.\n"; + usage(); return 1; } diff --git a/LaunchAPPL/LaunchAPPL.cfg.example b/LaunchAPPL/LaunchAPPL.cfg.example new file mode 100644 index 0000000000..5bb7e0780a --- /dev/null +++ b/LaunchAPPL/LaunchAPPL.cfg.example @@ -0,0 +1,69 @@ +# +# Example configuration file for LaunchAPPL +# Copy this file to $HOME/.LaunchAPPL.cfg and modify to your taste +# + +# ########### Classic Environment + + # If you are on a PowerPC Mac running Tiger (10.4), + # uncomment the following to use the Classic Environment: + +# emulator = classic + + +# ########### Carbon on Mac OS X (native PowerPC or Rosetta) + + # If you are on any Mac running Snow Leopard (10.6) or earlier + # and you are developing Carbon applications, use the following: + +# emulator = carbon + + +# ########### Mini vMac (old 68K Macs) + + # To use Mini vMac with LaunchAPPL, you need to supply the ROM file, + # a system disk image, and a download of autoquit from the minivmac web + # site, currently at + # http://www.gryphel.com/c/minivmac/extras/autoquit/index.html + # LaunchAPPL does not currently support MultiFinder or System 7. + +# Fill in the information below and uncomment the lines: + +# emulator = minivmac + + # The directory containing vMac.ROM + # All other paths relevant to Mini vMac are relative to this directory. +# minivmac-dir = /path/to/directory/with/vMac.ROM/ + + # First, we need Mini vMac itself: +# minivmac-path = ./Mini vMac + + # Next, a system disk image (System 6 or earlier) +# system-image = ./System.dsk + + # And finally, autoquit: +# autoquit-image = ./autoquit-1.1.1.dsk + +# ########### Executor (68K only) + + # No ROM files needed - an opensource reimplementation of classic Mac OS. + +# emulator = executor + + # If Executor is in your PATH and the SystemFolder environment variable + # is already set up, nothing else is required. + + # in case it's somewhere else: +# executor-path = /usr/local/bin/executor + + # Path to the Executor system folder: +# executor-system-folder = /path/to/ExecutorVolume/System Folder + + # Pass more options to Executor. + # Note that each "word" needs to be specified separately: +# executor-option = -size # emulated screen size +# executor-option = 1600x1200 + +# executor-option = -appearance # uncommenting these two lines +# executor-option = windows # is seriously not recommended. + diff --git a/LaunchAPPL/LaunchMethod.cc b/LaunchAPPL/LaunchMethod.cc index 34560d31db..b79928552c 100644 --- a/LaunchAPPL/LaunchMethod.cc +++ b/LaunchAPPL/LaunchMethod.cc @@ -1,4 +1,8 @@ #include "LaunchMethod.h" +#include +#include + +namespace fs = boost::filesystem; LaunchMethod::LaunchMethod() { @@ -18,3 +22,39 @@ bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options) { return true; } + +bool LaunchMethod::CheckExecutable(std::string program) +{ + if(access(program.c_str(), X_OK) == 0) + return true; + if(program.find("/") != std::string::npos) + return false; + const char *PATH = getenv("PATH"); + + if(PATH) + { + bool endFound = false; + do + { + const char *end = strchr(PATH, ':'); + if(!end) + { + end = strchr(PATH, '\0'); + endFound = true; + } + std::string pathElement(PATH, end); + + if(pathElement == "") + pathElement = "."; + + fs::path f = fs::path(pathElement) / program; + + if(access(f.string().c_str(), X_OK) == 0) + return true; + + PATH = end + 1; + } while(!endFound); + } + + return false; +} diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h index ab208db8e0..5fa1eb53f9 100644 --- a/LaunchAPPL/LaunchMethod.h +++ b/LaunchAPPL/LaunchMethod.h @@ -23,6 +23,9 @@ public: virtual bool CheckOptions(variables_map& options); virtual std::unique_ptr MakeLauncher(variables_map& options) = 0; + +protected: + bool CheckExecutable(std::string program); }; #endif // LAUNCHMETHOD_H diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index ac42ed035d..15eb341dbb 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -20,11 +20,13 @@ class MiniVMacLauncher : public Launcher { fs::path imagePath; fs::path systemImage; + fs::path vmacDir; + fs::path vmacPath; hfsvol *sysvol; hfsvol *vol; - void CopySystemFile(const char* fn, const char *dstfn = NULL); + void CopySystemFile(const std::string& fn, bool required); public: MiniVMacLauncher(po::variables_map& options); virtual ~MiniVMacLauncher(); @@ -38,9 +40,20 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) : Launcher(options, ResourceFile::Format::percent_appledouble), sysvol(NULL), vol(NULL) { - imagePath = fs::absolute(tempDir / "image.dsk"); - systemImage = fs::absolute(options["system-image"].as()); - fs::path autoquitImage = fs::absolute(options["autoquit-image"].as()); + imagePath = tempDir / "image.dsk"; + vmacDir = fs::absolute( options["minivmac-dir"].as() ); + vmacPath = fs::absolute( options["minivmac-path"].as(), vmacDir ); + + systemImage = fs::absolute(options["system-image"].as(), vmacDir); + fs::path autoquitImage = fs::absolute(options["autoquit-image"].as(), vmacDir); + + std::vector bootblock1(1024); + fs::ifstream(systemImage).read((char*) bootblock1.data(), 1024); + + if(bootblock1[0] != 'L' || bootblock1[1] != 'K' || bootblock1[0xA] > 15) + throw std::runtime_error("Not a bootable Mac disk image: " + systemImage.string()); + + string systemFileName(bootblock1.begin() + 0xB, bootblock1.begin() + 0xB + bootblock1[0xA]); int size = 5000*1024; @@ -49,10 +62,6 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) hfs_format(imagePath.string().c_str(), 0, 0, "SysAndApp", 0, NULL); { - std::vector bootblock1(1024); - - fs::ifstream(systemImage).read((char*) bootblock1.data(), 1024); - bootblock1[0x1A] = 8; memcpy(&bootblock1[0x1B],"AutoQuit", 8); bootblock1[0x5A] = 3; @@ -79,9 +88,8 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) - CopySystemFile("System"); - CopySystemFile("Finder"); - CopySystemFile("MacsBug"); + CopySystemFile(systemFileName, true); + CopySystemFile("MacsBug", false); { std::ostringstream rsrcOut; @@ -99,8 +107,10 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) hfs_umount(sysvol); sysvol = hfs_mount(autoquitImage.string().c_str(),0, HFS_MODE_RDONLY); + if(!sysvol) + throw std::runtime_error("Cannot open disk image: " + autoquitImage.string()); assert(sysvol); - CopySystemFile("AutoQuit"); + CopySystemFile("AutoQuit", true); { hfsfile *file = hfs_create(vol, "out", "TEXT", "MPS "); @@ -120,15 +130,15 @@ MiniVMacLauncher::~MiniVMacLauncher() } -void MiniVMacLauncher::CopySystemFile(const char *fn, const char *dstfn) +void MiniVMacLauncher::CopySystemFile(const std::string &fn, bool required) { - if(!dstfn) - dstfn = fn; hfsdirent fileent; - if(hfs_stat(sysvol, fn, &fileent) < 0) - return; - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, dstfn, fileent.u.file.type,fileent.u.file.creator); + if(hfs_stat(sysvol, fn.c_str(), &fileent) < 0) + { + throw std::runtime_error(string("File ") + fn + " not found in disk image"); + } + hfsfile *in = hfs_open(sysvol, fn.c_str()); + hfsfile *out = hfs_create(vol, fn.c_str(), fileent.u.file.type,fileent.u.file.creator); std::vector buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize)); hfs_setfork(in, 0); @@ -160,8 +170,6 @@ void MiniVMacLauncher::DumpOutput() vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY); hfsdirent fileent; int statres = hfs_stat(vol, "out", &fileent); - std::cerr << "stat: " << statres << "\n"; - std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; hfsfile *out = hfs_open(vol, "out"); if(!out) From 9a50b295817337915efe597ba9759bc6cfff5c90 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 18:44:23 +0200 Subject: [PATCH 13/29] ResourceFile: error handling... --- ResourceFiles/ResourceFile.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index ba9b332ed8..7021566ee1 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -217,11 +217,14 @@ bool ResourceFile::read() case Format::basilisk: { fs::ifstream dataIn(path); + if(!dataIn) + return false; data = std::string(std::istreambuf_iterator(dataIn), std::istreambuf_iterator()); fs::ifstream rsrcIn(path.parent_path() / ".rsrc" / path.filename()); - resources = Resources(rsrcIn); + if(rsrcIn) + resources = Resources(rsrcIn); fs::ifstream finfIn(path.parent_path() / ".finf" / path.filename()); if(finfIn) { @@ -234,10 +237,13 @@ bool ResourceFile::read() case Format::real: { fs::ifstream dataIn(path); + if(!dataIn) + return false; data = std::string(std::istreambuf_iterator(dataIn), std::istreambuf_iterator()); fs::ifstream rsrcIn(path / "..namedfork" / "rsrc"); - resources = Resources(rsrcIn); + if(rsrcIn) + resources = Resources(rsrcIn); char finf[32]; int n = getxattr(path.c_str(), XATTR_FINDERINFO_NAME, From 9732a267820c072afd5f315e0c9bd00b5f7ac872 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 19:36:49 +0200 Subject: [PATCH 14/29] AutomatedTests: remove hard-coded config in CMakeLists. --- AutomatedTests/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 3cfb851fca..c485f958fd 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -2,9 +2,7 @@ enable_testing() set(RETRO68_LAUNCH_METHOD classic CACHE String "How to launch Mac applications (for automated testing)") -set(RETRO68_TEST_CONFIG "-t4" "--minivmac-dir=/home/wolfgang/Emulators" - "--system-image=/home/wolfgang/Emulators/baresystem.dsk" - "--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk") +set(RETRO68_TEST_CONFIG "--timeout=4") find_program(LAUNCH_APPL LaunchAPPL PATH "${CMAKE_INSTALL_PREFIX}/../bin/") From 05cb15ae5b81da51fad4b1be6ee57f61429ddedb Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 20:09:03 +0200 Subject: [PATCH 15/29] missing #include --- LaunchAPPL/Launcher.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc index 41038f7278..f5b568f2ec 100644 --- a/LaunchAPPL/Launcher.cc +++ b/LaunchAPPL/Launcher.cc @@ -4,6 +4,7 @@ #include #include #include +#include namespace fs = boost::filesystem; From 5808ab19f6adadccf138d4c3cfb2eb104c306bbd Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 20:51:53 +0200 Subject: [PATCH 16/29] LaunchAPPL/MiniVMac: minor fixes --- LaunchAPPL/MiniVMac.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 15eb341dbb..611148f3d7 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -37,7 +37,7 @@ public: MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) - : Launcher(options, ResourceFile::Format::percent_appledouble), + : Launcher(options), sysvol(NULL), vol(NULL) { imagePath = tempDir / "image.dsk"; @@ -135,7 +135,10 @@ void MiniVMacLauncher::CopySystemFile(const std::string &fn, bool required) hfsdirent fileent; if(hfs_stat(sysvol, fn.c_str(), &fileent) < 0) { - throw std::runtime_error(string("File ") + fn + " not found in disk image"); + if(required) + throw std::runtime_error(string("File ") + fn + " not found in disk image"); + else + return; } hfsfile *in = hfs_open(sysvol, fn.c_str()); hfsfile *out = hfs_create(vol, fn.c_str(), fileent.u.file.type,fileent.u.file.creator); From 39112683a3eb7cad5f1170fb51e20dd09b82a790 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 22:26:24 +0200 Subject: [PATCH 17/29] LaunchAPPL: make minivmac backend work with the mac version --- LaunchAPPL/LaunchAPPL.cfg.example | 26 ++++--- LaunchAPPL/Launcher.cc | 4 + LaunchAPPL/MiniVMac.cc | 125 ++++++++++++++++++++++++++++-- 3 files changed, 136 insertions(+), 19 deletions(-) diff --git a/LaunchAPPL/LaunchAPPL.cfg.example b/LaunchAPPL/LaunchAPPL.cfg.example index 5bb7e0780a..4da1ff0d01 100644 --- a/LaunchAPPL/LaunchAPPL.cfg.example +++ b/LaunchAPPL/LaunchAPPL.cfg.example @@ -6,7 +6,7 @@ # ########### Classic Environment # If you are on a PowerPC Mac running Tiger (10.4), - # uncomment the following to use the Classic Environment: + # uncomment the following to use the Classic Environment: # emulator = classic @@ -14,7 +14,7 @@ # ########### Carbon on Mac OS X (native PowerPC or Rosetta) # If you are on any Mac running Snow Leopard (10.6) or earlier - # and you are developing Carbon applications, use the following: + # and you are developing Carbon applications, use the following: # emulator = carbon @@ -22,21 +22,25 @@ # ########### Mini vMac (old 68K Macs) # To use Mini vMac with LaunchAPPL, you need to supply the ROM file, - # a system disk image, and a download of autoquit from the minivmac web - # site, currently at - # http://www.gryphel.com/c/minivmac/extras/autoquit/index.html - # LaunchAPPL does not currently support MultiFinder or System 7. + # a system disk image, and a download of autoquit from the minivmac web + # site, currently at + # http://www.gryphel.com/c/minivmac/extras/autoquit/index.html + # LaunchAPPL does not currently support MultiFinder or System 7. # Fill in the information below and uncomment the lines: # emulator = minivmac - # The directory containing vMac.ROM - # All other paths relevant to Mini vMac are relative to this directory. + # All minivmac related paths are specified relative to minivmac-dir: # minivmac-dir = /path/to/directory/with/vMac.ROM/ # First, we need Mini vMac itself: # minivmac-path = ./Mini vMac + # On Macs, specify the path of the application bundle, not the executable inside it: +# minivmac-path = ./Mini vMac.app + + # A ROM file: +# minivmac-rom = ./vMac.ROM # Next, a system disk image (System 6 or earlier) # system-image = ./System.dsk @@ -51,7 +55,7 @@ # emulator = executor # If Executor is in your PATH and the SystemFolder environment variable - # is already set up, nothing else is required. + # is already set up, nothing else is required. # in case it's somewhere else: # executor-path = /usr/local/bin/executor @@ -60,8 +64,8 @@ # executor-system-folder = /path/to/ExecutorVolume/System Folder # Pass more options to Executor. - # Note that each "word" needs to be specified separately: -# executor-option = -size # emulated screen size + # Note that each "word" needs to be specified separately: +# executor-option = -size # emulated screen size # executor-option = 1600x1200 # executor-option = -appearance # uncommenting these two lines diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc index f5b568f2ec..3f21a2ec47 100644 --- a/LaunchAPPL/Launcher.cc +++ b/LaunchAPPL/Launcher.cc @@ -73,6 +73,10 @@ int Launcher::ChildProcess(string program, vector args, int timeout) { execvp(argv[0], const_cast (argv.data())); perror("exec failed"); + std::cerr << "Tried to execute: " << program; + for(auto a : args) + std::cerr << " " << a; + std::cerr << std::endl; _exit(1); } diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 611148f3d7..6a564f6f85 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -9,6 +9,12 @@ extern "C" { #include #include + +#ifdef __APPLE__ +#define ResType MacResType +#include +#endif + namespace fs = boost::filesystem; using std::string; using std::vector; @@ -36,11 +42,37 @@ public: }; +/* + * Recursive directory copy from https://stackoverflow.com/a/39146566 + */ +static void copyDirectoryRecursively(const fs::path& sourceDir, const fs::path& destinationDir) +{ + if (!fs::exists(sourceDir) || !fs::is_directory(sourceDir)) + { + throw std::runtime_error("Source directory " + sourceDir.string() + " does not exist or is not a directory"); + } + if (fs::exists(destinationDir)) + { + throw std::runtime_error("Destination directory " + destinationDir.string() + " already exists"); + } + if (!fs::create_directory(destinationDir)) + { + throw std::runtime_error("Cannot create destination directory " + destinationDir.string()); + } + + for (const auto& dirEnt : fs::recursive_directory_iterator{sourceDir}) + { + const auto& path = dirEnt.path(); + auto relativePathStr = path.lexically_relative(sourceDir); + fs::copy(path, destinationDir / relativePathStr); + } +} + MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) : Launcher(options), sysvol(NULL), vol(NULL) { - imagePath = tempDir / "image.dsk"; + imagePath = tempDir / "disk1.dsk"; vmacDir = fs::absolute( options["minivmac-dir"].as() ); vmacPath = fs::absolute( options["minivmac-path"].as(), vmacDir ); @@ -119,6 +151,86 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) hfs_umount(sysvol); sysvol = NULL; hfs_umount(vol); vol = NULL; + + fs::path romFile = fs::absolute( options["minivmac-rom"].as(), vmacDir ); + + fs::create_symlink( + romFile, + tempDir / romFile.filename() ); + + if(romFile.filename() != "vMac.ROM") + { + // If the ROM file is not named vMac.ROM, this might be for two different + // reasons. + // 1. The user didn't bother to rename it to the correct "vMac.ROM" + // 2. The user is using a MacII version of Mini vMac and has named the + // ROM file MacII.ROM on purpose. + + // To be on the safe side, provide both the user-specified name and + // the standard vMac.ROM. + + fs::create_symlink( + romFile, + tempDir / romFile.filename() ); + } + +#ifdef __APPLE__ + /* + A special case for the Mac. + + The Mac build of Mini vMac does not look for files (vMac.ROM, disk1.dsk) + in the current directory, but rather in the parent directory + of the .app bundle. + + Also, it ignores command line arguments. + + So we just copy the entire application bundle over to our temporary + directory. It is five times smaller than System 6, so this really does not + matter. + */ + if(vmacPath.extension().string() == ".app") + { + fs::path appPath = tempDir / "minivmac.app"; + + copyDirectoryRecursively( vmacPath, appPath ); + + // The following 30 lines of code should rather be written as: + // vmacPath = appPath / "Contents" / "MacOS" / Bundle(appPath).getExecutablePath(); + // But this is CoreFoundation, so it's a tiny little bit more verbose: + + CFStringRef appPathCF + = CFStringCreateWithCString( + kCFAllocatorDefault, appPath.string().c_str(), kCFStringEncodingUTF8); + CFURLRef bundleURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, appPathCF, kCFURLPOSIXPathStyle, true); + + CFBundleRef bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); + + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + CFStringRef executablePath = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle); + + if(const char *ptr = CFStringGetCStringPtr(executablePath, kCFURLPOSIXPathStyle)) + { + vmacPath = string(ptr); + } + else + { + vector buffer( + CFStringGetMaximumSizeForEncoding( + CFStringGetLength(executablePath), kCFStringEncodingUTF8) + 1); + CFStringGetCString(executablePath, buffer.data(), buffer.size(), kCFStringEncodingUTF8); + vmacPath = string(buffer.data()); + } + vmacPath = appPath / "Contents" / "MacOS" / vmacPath; + + CFRelease(appPathCF); + CFRelease(bundleURL); + CFRelease(bundle); + CFRelease(executableURL); + CFRelease(executablePath); + } +#endif } MiniVMacLauncher::~MiniVMacLauncher() @@ -159,17 +271,12 @@ void MiniVMacLauncher::CopySystemFile(const std::string &fn, bool required) bool MiniVMacLauncher::Go(int timeout) { - fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); - std::string minivmacpath = options["minivmac-path"].as(); - - fs::current_path(minivmacdir); - - return ChildProcess(minivmacpath, { imagePath.string() }, timeout) == 0; + fs::current_path(tempDir); + return ChildProcess(vmacPath.string(), {}, timeout) == 0; } void MiniVMacLauncher::DumpOutput() { - sleep(1); vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY); hfsdirent fileent; int statres = hfs_stat(vol, "out", &fileent); @@ -191,6 +298,7 @@ void MiniVMac::GetOptions(options_description &desc) desc.add_options() ("minivmac-dir", po::value(),"directory containing vMac.ROM") ("minivmac-path", po::value()->default_value("./minivmac"),"relative path to minivmac") + ("minivmac-rom", po::value()->default_value("./vMac.ROM"),"minivmac ROM file") ("system-image", po::value(),"path to disk image with system") ("autoquit-image", po::value(),"path to autoquit disk image, available from the minivmac web site") ; @@ -200,6 +308,7 @@ bool MiniVMac::CheckOptions(variables_map &options) { return options.count("minivmac-path") != 0 && options.count("minivmac-dir") != 0 + && options.count("minivmac-rom") != 0 && options.count("system-image") != 0 && options.count("autoquit-image") != 0; } From f9bcc39ce063ee9d82425046868d574a599637ae Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 22:47:31 +0200 Subject: [PATCH 18/29] LaunchAPPL/MiniVMac: fix the fix :-) --- LaunchAPPL/MiniVMac.cc | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 6a564f6f85..d1bc432e28 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -174,19 +174,19 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) tempDir / romFile.filename() ); } + /* + Finally, we copy over the entire Mini vMac binary. + Mini vMac looks for ROM (vMac.ROM) and disk images (disk1.dsk) + in the directory next to its binary. + The Mac version also ignores command line arguments. + Having our own copy in our temp directory is just simpler. + It is five times smaller than System 6, so this really does not + matter. + */ #ifdef __APPLE__ /* - A special case for the Mac. - - The Mac build of Mini vMac does not look for files (vMac.ROM, disk1.dsk) - in the current directory, but rather in the parent directory - of the .app bundle. - - Also, it ignores command line arguments. - - So we just copy the entire application bundle over to our temporary - directory. It is five times smaller than System 6, so this really does not - matter. + A special case for the Mac: + We are probably dealing with an entire application bundle. */ if(vmacPath.extension().string() == ".app") { @@ -230,7 +230,12 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) CFRelease(executableURL); CFRelease(executablePath); } + else #endif + { + fs::copy(vmacPath, tempDir / "minivmac"); + vmacPath = tempDir / "minivmac"; + } } MiniVMacLauncher::~MiniVMacLauncher() From bd38a209ba7d3bf495624c58127940fd299b121d Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Wed, 4 Oct 2017 01:56:05 +0200 Subject: [PATCH 19/29] AutomatedTests improvements --- AutomatedTests/CMakeLists.txt | 6 +++- AutomatedTests/Empty.c | 1 + AutomatedTests/File.c | 2 +- AutomatedTests/Log.c | 6 +++- AutomatedTests/ReallyEmpty.c | 2 ++ AutomatedTests/Test.h | 61 +++++++++++++++++++++++++++----- AutomatedTests/ZeroInitialized.c | 42 ++++++++++++++++++++++ 7 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 AutomatedTests/ZeroInitialized.c diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index c485f958fd..5367f3141d 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -24,11 +24,15 @@ test(File.c) set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") test(Timeout.c) -set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo") +set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One") test(Log.c) set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") +test(ZeroInitialized.c) +set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") + + test(Init.cc) set_tests_properties(Init PROPERTIES PASS_REGULAR_EXPRESSION "constructor\nmain\ndestructor") diff --git a/AutomatedTests/Empty.c b/AutomatedTests/Empty.c index a46866d92e..7e05b9a6a8 100644 --- a/AutomatedTests/Empty.c +++ b/AutomatedTests/Empty.c @@ -1,4 +1,5 @@ int main() { + // Test: do things work well enough for us to get to main()? return 0; } diff --git a/AutomatedTests/File.c b/AutomatedTests/File.c index 1c2595a4b5..e4be336294 100644 --- a/AutomatedTests/File.c +++ b/AutomatedTests/File.c @@ -2,5 +2,5 @@ int main() { - TEST_LOG_SIZED("OK", 2); + TEST_LOG_OK(); } diff --git a/AutomatedTests/Log.c b/AutomatedTests/Log.c index 8c216dde56..3c3195f8ba 100644 --- a/AutomatedTests/Log.c +++ b/AutomatedTests/Log.c @@ -1,9 +1,13 @@ #include "Test.h" +char readWriteData[6] = "Three"; + int main() { + // constant initialized data TEST_LOG_SIZED("One",3); TEST_LOG_SIZED("Two",3); - TEST_LOG_SIZED("Three",5); + // read-write initialized data + TEST_LOG_SIZED(readWriteData,5); return 0; } diff --git a/AutomatedTests/ReallyEmpty.c b/AutomatedTests/ReallyEmpty.c index 81e5f841aa..1f26045306 100644 --- a/AutomatedTests/ReallyEmpty.c +++ b/AutomatedTests/ReallyEmpty.c @@ -1,4 +1,6 @@ void _start() { + // Test: do things work well enough for us to get to a startup function? + // Note: this won't work for multisegment 68K apps, as the startup function will be in the wrong segment. } diff --git a/AutomatedTests/Test.h b/AutomatedTests/Test.h index b43e622931..3e88d860a3 100644 --- a/AutomatedTests/Test.h +++ b/AutomatedTests/Test.h @@ -5,10 +5,25 @@ #include #include +/* + Log test output to a file called 'out' in the current directory. + + Most of this is implemented as macros, in a very cumbersome, low-level way, + avoiding the use of function calls, string constants or global variables. + This way, we only test what we want to test. + */ + +/* The "high level" variant - log a string. */ +#ifdef __cplusplus +extern "C" +#endif +void TestLog(const char *str); + +/* The same thing as a macro. String length has to be given explicitly, + * to avoid a call to strlen(). */ #define TEST_LOG_SIZED(str, size) \ do { \ HParamBlockRec _hpb; \ - memset(&_hpb,0,sizeof(_hpb)); \ \ unsigned char _fileName[4]; \ short _ref;\ @@ -22,33 +37,61 @@ _hpb.ioParam.ioVRefNum = 0; \ _hpb.fileParam.ioDirID = 0; \ _hpb.ioParam.ioPermssn = fsRdWrPerm; \ + _hpb.ioParam.ioMisc = NULL; \ PBHOpenSync(&_hpb); \ _ref = _hpb.ioParam.ioRefNum; \ \ - memset(&_hpb,0,sizeof(_hpb)); \ + _hpb.ioParam.ioCompletion = NULL; \ _hpb.ioParam.ioBuffer = (Ptr)str; \ _hpb.ioParam.ioReqCount = size; \ _hpb.ioParam.ioPosMode = fsFromLEOF; \ _hpb.ioParam.ioPosOffset = 0; \ _hpb.ioParam.ioRefNum = _ref; \ + _hpb.ioParam.ioMisc = NULL; \ PBWriteSync((void*)&_hpb); \ - memset(&_hpb,0,sizeof(_hpb)); \ char _newline = '\n'; \ + _hpb.ioParam.ioCompletion = NULL; \ _hpb.ioParam.ioBuffer = &_newline; \ _hpb.ioParam.ioReqCount = 1; \ _hpb.ioParam.ioPosMode = fsFromLEOF; \ _hpb.ioParam.ioPosOffset = 0; \ _hpb.ioParam.ioRefNum = _ref; \ + _hpb.ioParam.ioMisc = NULL; \ PBWriteSync((void*)&_hpb); \ - memset(&_hpb,0,sizeof(_hpb)); \ + _hpb.ioParam.ioCompletion = NULL; \ _hpb.ioParam.ioRefNum = _ref; \ + _hpb.ioParam.ioMisc = NULL; \ PBCloseSync((void*)&_hpb); \ - FlushVol(NULL,0); \ + _hpb.ioParam.ioCompletion = NULL; \ + _hpb.ioParam.ioNamePtr = NULL; \ + _hpb.ioParam.ioVRefNum = 0; \ + _hpb.ioParam.ioMisc = NULL; \ + PBFlushVolSync((void*)&_hpb); \ } while(0); -#ifdef __cplusplus -extern "C" -#endif -void TestLog(const char *str); +/* + * Output either "OK" or "NO". + * String constants are off-limits, + * we might not want to test them yet. + */ + +#define TEST_LOG_OK() \ + do { \ + char ok[3]; \ + ok[0] = 'O'; \ + ok[1] = 'K'; \ + ok[2] = '\0'; \ + TEST_LOG_SIZED(ok, 2); \ + } while(0) + +#define TEST_LOG_NO() \ + do { \ + char no[3]; \ + no[0] = 'O'; \ + no[1] = 'K'; \ + no[2] = '\0'; \ + TEST_LOG_SIZED(no, 2); \ + } while(0) + #endif // TEST_H diff --git a/AutomatedTests/ZeroInitialized.c b/AutomatedTests/ZeroInitialized.c new file mode 100644 index 0000000000..288717bd13 --- /dev/null +++ b/AutomatedTests/ZeroInitialized.c @@ -0,0 +1,42 @@ +#include "Test.h" +#include + +int zeroInitedArray[32768]; +int commonSymbol; +int zeroInited = 0; +EventRecord e; + +int main() +{ + int i; + if(commonSymbol) + { + TEST_LOG_NO(); + return 1; + } + if(zeroInited) + { + TEST_LOG_NO(); + return 1; + } + for(i = 0; i < 32768; i++) + { + if(zeroInitedArray[i]) + { + TEST_LOG_NO(); + return 1; + } + zeroInitedArray[i] = 42; + } + GetNextEvent(everyEvent, &e); + for(i = 0; i < 32768; i++) + { + if(zeroInitedArray[i] != 42) + { + TEST_LOG_NO(); + return 1; + } + } + TEST_LOG_OK(); + return 0; +} From 507ba9debff391d24885fc7ff46127453b3c3e64 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Wed, 4 Oct 2017 02:03:26 +0200 Subject: [PATCH 20/29] LaunchAPPL --make-executable: add a #!/.../LaunchCFMApp to a mac app --- LaunchAPPL/CMakeLists.txt | 1 + LaunchAPPL/LaunchAPPL.cc | 23 ++++++++++++++-- LaunchAPPL/MakeExecutable.cc | 52 +++++++++++++++++++++++++++++++++++ ResourceFiles/ResourceFile.cc | 21 ++++++++++++++ ResourceFiles/ResourceFile.h | 3 ++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 LaunchAPPL/MakeExecutable.cc diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 30e846322d..ce0bc61630 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -20,6 +20,7 @@ add_definitions(-DRETRO68_PREFIX="${CMAKE_INSTALL_PREFIX}") add_executable(LaunchAPPL LaunchAPPL.cc + MakeExecutable.cc LaunchMethod.h LaunchMethod.cc Launcher.h Launcher.cc diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 38f391b9d6..af532d75ea 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -93,7 +93,7 @@ static void usage() } } - +void MakeExecutable(string filepath); int main(int argc, char *argv[]) { @@ -102,6 +102,7 @@ int main(int argc, char *argv[]) desc.add_options() ("help,h", "show this help message") + ("make-executable,x", po::value(), "make a MacBinary file executable") ; po::options_description configdesc; configdesc.add_options() @@ -158,12 +159,28 @@ int main(int argc, char *argv[]) po::notify(options); - if(options.count("help") || !options.count("application") || !options.count("emulator")) + if(options.count("help") || (!options.count("application") && !options.count("make-executable"))) { usage(); return 0; } + if(options.count("make-executable")) + { + string fn = options["make-executable"].as(); + MakeExecutable(fn); + + if(!options.count("application")) + return 0; + } + + if(!options.count("emulator")) + { + std::cerr << "ERROR: emulator/environment not specified.\n"; + usage(); + return 1; + } + LaunchMethod *method = NULL; for(LaunchMethod *lm : launchMethods) { @@ -181,7 +198,7 @@ int main(int argc, char *argv[]) if(!method->CheckOptions(options)) { - std::cerr << "Missing configuration.\n"; + std::cerr << "Need more configuration.\n"; usage(); return 1; } diff --git a/LaunchAPPL/MakeExecutable.cc b/LaunchAPPL/MakeExecutable.cc new file mode 100644 index 0000000000..b2f08304b5 --- /dev/null +++ b/LaunchAPPL/MakeExecutable.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "ResourceFile.h" + +using std::string; +namespace fs = boost::filesystem; + +void MakeExecutable(string fn) +{ + ResourceFile rsrcFile(fn); + if(!rsrcFile.read()) + { + std::cerr << "Cannot read application file: " << fn << std::endl; + exit(1); + } + if(!rsrcFile.hasPlainDataFork()) + { + std::cerr << "--make-executable can not be used with this data format.\n"; + exit(1); + } + + string headerString = "#!" RETRO68_PREFIX "/bin/LaunchAPPL\n"; + + bool hadShebang = false; + if(rsrcFile.data.size()) + { + if(headerString.substr(2) == "#!") + { + string::size_type eol = headerString.find('\n'); + if(eol != string::npos && eol >= 13 && eol < 4096) + { + if(headerString.substr(eol-11,11) == "/LaunchAPPL") + hadShebang = true; + } + } + + if(!hadShebang) + { + std::cerr << "Unfortunately, the application already has a data fork.\n"; + std::cerr << "LaunchAPPL --make-executable does not currently work for PowerPC apps.\n"; + // TODO: if it's a PEF container, move it back a little and update cfrg + exit(1); + } + } + + std::fstream(fn, std::ios::in | std::ios::out | std::ios::binary) << headerString; + + fs::permissions(fs::path(fn), fs::owner_exe | fs::group_exe | fs::others_exe | fs::add_perms); +} diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index 7021566ee1..fb86aef9f4 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -523,3 +523,24 @@ bool ResourceFile::write() return true; } +bool ResourceFile::hasPlainDataFork(ResourceFile::Format f) +{ + switch(f) + { +#ifdef __APPLE__ + case Format::real: +#endif + case Format::basilisk: + case Format::underscore_appledouble: + case Format::percent_appledouble: + return true; + default: + return false; + } +} + +bool ResourceFile::hasPlainDataFork() +{ + return hasPlainDataFork(format); +} + diff --git a/ResourceFiles/ResourceFile.h b/ResourceFiles/ResourceFile.h index f31dd4e319..00cea3f0f6 100644 --- a/ResourceFiles/ResourceFile.h +++ b/ResourceFiles/ResourceFile.h @@ -31,6 +31,9 @@ public: bool read(); bool write(); + static bool hasPlainDataFork(Format f); + bool hasPlainDataFork(); + std::string pathstring; Format format; ResType type; From d8ad527e0f3190b78b41c89430a27f4cc752822d Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Wed, 4 Oct 2017 16:13:00 +0200 Subject: [PATCH 21/29] Update documentation --- README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dad220f72a..3e52a8ee5a 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Third Party Components: - binutils 2.28 - gcc 6.3.0 - newlib 2.10.1 (inside the gcc directory) -- elf2flt (from the ucLinux project's CVS) +- libelf from elfutils-0.170 - hfsutils 3.2.6 Retro68-Specific Components: @@ -134,6 +134,7 @@ Retro68-Specific Components: - Rez - PEFTools (MakePEF and MakeImport) - MakeAPPL +- LaunchAPPL - libretro - TestApps - a few tiny test programs - Sample Programs: Raytracer, HelloWorld, Launcher, Dialog @@ -145,6 +146,7 @@ Two new target platforms: - `powerpc-apple-macos`, based on the `powerpc-ibm-aix` target The powerpc target has a few hacks to make weak symbols work as expected. +The elf target has a hack to protect MacsBug symbols from -gc-sections. ### gcc @@ -169,10 +171,12 @@ PowerPC specific: Standard C library. Currently unmodified. The missing platform-dependent bits haven't been added, instead they are found in 'libretro'. -### elf2flt +### libelf -Converts from ELF to a much simpler binary format. -Minor patch: provide symbols around .init and .fini sections +A library for convenient access to ELF files, taken from the elfutils-0.170 +package. Or rather, brutally ripped out of it, hacked to compile on non-linux +platforms ( is not a standard header file), and made to build with +cmake instead of autotools. Much simpler now. ### hfsutils: @@ -188,6 +192,26 @@ A reimplementation of Apple's Rez resource compiler. Reads `.r` files containing textual resource descriptions and compiles them to binary resource files. +### Elf2Mac + +A wrapper around the linker for 68K programs; it supplies a linker script, +invokes the linker, and converts the resulting ELF binary to a Mac APPL with +one or more segments, or to a flat file which can be converted to a code resource +using Rez. + +### LaunchAPPL + +A tool for lauching compiled Mac applications via various emulators. +Currently, there are the following backends: + +* classic - launch in the Classic environment on PowerPC Macs up to Tiger (10.4) +* carbon - launch as a Carbon app on PowerPC Macs and via Rosetta on Intel Macs up to Snow Leopard (10.6) +* minivmac - launch using the Mini vMac emulator +* executor - launch using Executor + +**CONTRIBUTION OPPORTUNITY** - This tool can easily be extended with further backends, +so make it work with your favourtite emulator. + ### ConvertObj Reads a MPW 68K Object file (`*.o`) and converts it to input for the @@ -228,6 +252,10 @@ for some standard library functions. Contains a library that implements basic text console functionality. +### AutomatedTests + +An automated test suite that can be run using `ctest` and `LaunchAPPL`. + ### Sample Program: Hello World The binary is in Retro68-build/build-target/Samples/HelloWorld/. From d4f3670056041e8cb87bb20ab5a3a8d9dd5d68db Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Wed, 4 Oct 2017 17:34:00 +0200 Subject: [PATCH 22/29] AutomatedTests: set defaults for RETRO68_LAUNCH_METHOD on old Mac OS X platforms, where "classic" and "carbon" are the obvious choices --- AutomatedTests/CMakeLists.txt | 33 +++++++++++++++++++++++++----- CMakeLists.txt | 2 +- LaunchAPPL/LaunchAPPL.cc | 38 +++++++++++++++++++++++++++++------ README.md | 3 ++- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 5367f3141d..7b49a44b3c 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -1,10 +1,32 @@ -enable_testing() +cmake_minimum_required(VERSION 3.3) -set(RETRO68_LAUNCH_METHOD classic CACHE String "How to launch Mac applications (for automated testing)") - -set(RETRO68_TEST_CONFIG "--timeout=4") find_program(LAUNCH_APPL LaunchAPPL PATH "${CMAKE_INSTALL_PREFIX}/../bin/") +execute_process(COMMAND ${LAUNCH_APPL} --list-emulators + OUTPUT_VARIABLE EMULATOR_LIST) +separate_arguments(EMULATOR_LIST) + +if(CMAKE_SYSTEM_NAME MATCHES "RetroCarbon") + if("carbon" IN_LIST EMULATOR_LIST) + set(RETRO68_LAUNCH_METHOD carbon CACHE String "How to launch Mac applications (for automated testing)") + else() + set(RETRO68_LAUNCH_METHOD NONE CACHE String "How to launch Mac applications (for automated testing)") + endif() +else() + if("classic" IN_LIST EMULATOR_LIST) + set(RETRO68_LAUNCH_METHOD classic CACHE String "How to launch Mac applications (for automated testing)") + else() + set(RETRO68_LAUNCH_METHOD NONE CACHE String "How to launch Mac applications (for automated testing)") + endif() +endif() +set(RETRO68_TEST_CONFIG "--timeout=10" CACHE String "Options to pass to LaunchAPPL when running tests") + +if(RETRO68_LAUNCH_METHOD MATCHES "NONE") +else() # extends to end of file + +enable_testing() + + function(test FILE) get_filename_component(NAME ${FILE} NAME_WE) @@ -13,7 +35,7 @@ function(test FILE) -e ${RETRO68_LAUNCH_METHOD} ${RETRO68_TEST_CONFIG} ${ARGN} ${NAME}.bin) endfunction() -if(CMAKE_SYSTEM_NAME MATCHES Retro68) +if(CMAKE_SYSTEM_NAME MATCHES "Retro68") test(ReallyEmpty.c) set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single") endif() @@ -36,3 +58,4 @@ set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") test(Init.cc) set_tests_properties(Init PROPERTIES PASS_REGULAR_EXPRESSION "constructor\nmain\ndestructor") +endif() # RETRO68_LAUNCH_METHOD diff --git a/CMakeLists.txt b/CMakeLists.txt index d5514ae520..dd10799454 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with Retro68. If not, see . -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.3) project(Retro) set(CMAKE_CXX_STANDARD 11) diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index af532d75ea..64baf29173 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -102,6 +102,7 @@ int main(int argc, char *argv[]) desc.add_options() ("help,h", "show this help message") + ("list-emulators,l", "get the list of available, fully configured emulators/environments") ("make-executable,x", po::value(), "make a MacBinary file executable") ; po::options_description configdesc; @@ -159,19 +160,44 @@ int main(int argc, char *argv[]) po::notify(options); - if(options.count("help") || (!options.count("application") && !options.count("make-executable"))) + vector commandModes = {"application", "help", "make-executable", "list-emulators"}; + int nModes = 0; + string mode; + + for(string aMode : commandModes) + { + if(options.count(aMode)) + { + nModes++; + mode = aMode; + } + } + if(nModes > 1) + { + std::cerr << "Need to specify either an application file or exactly one of "; + for(int i = 1, n = commandModes.size(); i < n-1; i++) + std::cerr << "--" << commandModes[i] << ", "; + std::cerr << "or " << commandModes.back() << "." << std::endl << std::endl; + usage(); + return 1; + } + if(mode == "" || mode == "help") { usage(); return 0; } - - if(options.count("make-executable")) + else if(mode == "make-executable") { string fn = options["make-executable"].as(); MakeExecutable(fn); - - if(!options.count("application")) - return 0; + return 0; + } + else if(mode == "list-emulators") + { + for(LaunchMethod *method : launchMethods) + if(method->CheckOptions(options)) + std::cout << method->GetName() << std::endl; + return 0; } if(!options.count("emulator")) diff --git a/README.md b/README.md index 3e52a8ee5a..e7756fe3a9 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,8 @@ Sample programs are built in several formats: Look under `Retro68-build/build-target/` (68K), `Retro68-build/build-target-ppc/` (PowerPC Classic) and -`Retro68-build/build-target-carbon/` (PowerPC Carbon) for the compiled examples. +`Retro68-build/build-target-carbon/` (PowerPC Carbon) for the compiled examples, +especially under the `Samples` subdirectory. Components ---------- From da6426b20775d2a662956293b97890e4e32115a7 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 14:52:17 +0200 Subject: [PATCH 23/29] Tiger compatibility; most importantly, we want a C99 compiler --- AutomatedTests/CMakeLists.txt | 3 ++- CMakeLists.txt | 3 +++ LaunchAPPL/MakeExecutable.cc | 1 + LaunchAPPL/MiniVMac.cc | 2 +- README.md | 2 +- build-toolchain.bash | 6 +++++- 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 7b49a44b3c..e5038c0283 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.3) find_program(LAUNCH_APPL LaunchAPPL PATH "${CMAKE_INSTALL_PREFIX}/../bin/") execute_process(COMMAND ${LAUNCH_APPL} --list-emulators OUTPUT_VARIABLE EMULATOR_LIST) -separate_arguments(EMULATOR_LIST) +string(REPLACE "\n" ";" EMULATOR_LIST ${EMULATOR_LIST}) if(CMAKE_SYSTEM_NAME MATCHES "RetroCarbon") if("carbon" IN_LIST EMULATOR_LIST) @@ -19,6 +19,7 @@ else() set(RETRO68_LAUNCH_METHOD NONE CACHE String "How to launch Mac applications (for automated testing)") endif() endif() + set(RETRO68_TEST_CONFIG "--timeout=10" CACHE String "Options to pass to LaunchAPPL when running tests") if(RETRO68_LAUNCH_METHOD MATCHES "NONE") diff --git a/CMakeLists.txt b/CMakeLists.txt index dd10799454..62b549c54c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,9 @@ cmake_minimum_required(VERSION 3.3) project(Retro) set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED TRUE) if(CMAKE_SYSTEM_NAME MATCHES Retro.*) diff --git a/LaunchAPPL/MakeExecutable.cc b/LaunchAPPL/MakeExecutable.cc index b2f08304b5..5d3f5c58e6 100644 --- a/LaunchAPPL/MakeExecutable.cc +++ b/LaunchAPPL/MakeExecutable.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index d1bc432e28..ac0d2ae422 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -63,7 +63,7 @@ static void copyDirectoryRecursively(const fs::path& sourceDir, const fs::path& for (const auto& dirEnt : fs::recursive_directory_iterator{sourceDir}) { const auto& path = dirEnt.path(); - auto relativePathStr = path.lexically_relative(sourceDir); + auto relativePathStr = path.string().substr(sourceDir.string().size()); fs::copy(path, destinationDir / relativePathStr); } } diff --git a/README.md b/README.md index e7756fe3a9..5325cbb324 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ the build directory. If you're building this on a PowerMac running Mac OS X 10.4, tell the build script to use the gcc you've installed via tigerbrew: - ../Retro68/build-toolchain.bash --host-cxx-compiler=g++-5 + ../Retro68/build-toolchain.bash --host-cxx-compiler=g++-5 --host-c-compiler=gcc-5 ### Build options and recompiling diff --git a/build-toolchain.bash b/build-toolchain.bash index 55a75c2fbe..339acb2acf 100755 --- a/build-toolchain.bash +++ b/build-toolchain.bash @@ -64,7 +64,11 @@ for ARG in $*; do CLEAN_AFTER_BUILD=true ;; --host-cxx-compiler=*) - HOST_CMAKE_FLAGS[${#HOST_CMAKE_FLAGS}]="-DCMAKE_CXX_COMPILER=${ARG#*=}" + HOST_CMAKE_FLAGS[${#HOST_CMAKE_FLAGS[@]}]="-DCMAKE_CXX_COMPILER=${ARG#*=}" + ;; + --host-c-compiler=*) + HOST_CMAKE_FLAGS[${#HOST_CMAKE_FLAGS[@]}]="-DCMAKE_C_COMPILER=${ARG#*=}" + HOST_C_COMPILER="${ARG#*=}" ;; --help) usage From c6f6cddb682bfad1256112cc7159ddc3cbf86b4a Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 15:30:58 +0200 Subject: [PATCH 24/29] LaunchAPPL: improve detection for classic/carbon --- LaunchAPPL/CMakeLists.txt | 2 -- LaunchAPPL/Carbon.cc | 13 +++++++- LaunchAPPL/Carbon.h | 1 + LaunchAPPL/Classic.cc | 18 +++++++++-- LaunchAPPL/Classic.h | 1 + LaunchAPPL/LaunchAPPL.cc | 61 ++++++++++++++++++++++---------------- LaunchAPPL/LaunchMethod.cc | 5 ++++ LaunchAPPL/LaunchMethod.h | 2 ++ 8 files changed, 71 insertions(+), 32 deletions(-) diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index ce0bc61630..ad801680fa 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -9,9 +9,7 @@ if(APPLE) LIST(APPEND LAUNCHMETHODS Classic.h Classic.cc ) - find_program(LAUNCHCFMAPP LaunchCFMApp) if(LAUNCHCFMAPP) - add_definitions(-DHAS_LAUNCHCFMAPP) LIST(APPEND LAUNCHMETHODS Carbon.h Carbon.cc) endif() diff --git a/LaunchAPPL/Carbon.cc b/LaunchAPPL/Carbon.cc index 22ef6e92e6..9c36553c9c 100644 --- a/LaunchAPPL/Carbon.cc +++ b/LaunchAPPL/Carbon.cc @@ -1,6 +1,9 @@ #include "Carbon.h" #include "Launcher.h" +const std::string launchCFM = + "/System/Library/Frameworks/Carbon.framework/Versions/A/Support/LaunchCFMApp"; + namespace po = boost::program_options; class CarbonLauncher : public Launcher @@ -26,7 +29,15 @@ CarbonLauncher::~CarbonLauncher() bool CarbonLauncher::Go(int timeout) { - return ChildProcess("LaunchCarbon", { appPath.string() }, timeout) == 0; + return ChildProcess(launchCFM, { appPath.string() }, timeout) == 0; +} + +bool Carbon::CheckPlatform() +{ + /* If LaunchCFMApp doesn't exist, we're likely on a Mac OS X version + where it doesn't exist anymore (10.7 Lion or later), + or on an entirely different platform. */ + return CheckExecutable(launchCFM); } std::unique_ptr Carbon::MakeLauncher(variables_map &options) diff --git a/LaunchAPPL/Carbon.h b/LaunchAPPL/Carbon.h index f9c2ce219e..ff68272f47 100644 --- a/LaunchAPPL/Carbon.h +++ b/LaunchAPPL/Carbon.h @@ -8,6 +8,7 @@ class Carbon : public LaunchMethod public: virtual std::string GetName() { return "carbon"; } + virtual bool CheckPlatform(); virtual std::unique_ptr MakeLauncher(variables_map& options); }; diff --git a/LaunchAPPL/Classic.cc b/LaunchAPPL/Classic.cc index 71d5dc49fa..8d71ba0041 100644 --- a/LaunchAPPL/Classic.cc +++ b/LaunchAPPL/Classic.cc @@ -1,9 +1,11 @@ -#if defined(__APPLE__) && defined(__powerpc) +#define ResType MacResType +#include +#undef ResType + +#if TARGET_CPU_PPC #include "Classic.h" #include "Launcher.h" -#define ResType MacResType -#include namespace po = boost::program_options; @@ -63,6 +65,16 @@ bool ClassicLauncher::Go(int timeout) return false; } +bool Classic::CheckPlatform() +{ + long sysver = 0; + Gestalt(gestaltSystemVersion, &sysver); + if(sysver >= 0x1050) + return false; + else + return true; +} + std::unique_ptr Classic::MakeLauncher(variables_map &options) { return std::unique_ptr(new ClassicLauncher(options)); diff --git a/LaunchAPPL/Classic.h b/LaunchAPPL/Classic.h index 17b5627603..22d869c545 100644 --- a/LaunchAPPL/Classic.h +++ b/LaunchAPPL/Classic.h @@ -8,6 +8,7 @@ class Classic : public LaunchMethod public: virtual std::string GetName() { return "classic"; } + virtual bool CheckPlatform(); virtual std::unique_ptr MakeLauncher(variables_map& options); }; diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 64baf29173..3948eb7239 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -8,10 +8,13 @@ #include "LaunchMethod.h" #include "Launcher.h" -#if defined(__APPLE__) && defined(__powerpc) -# include "Classic.h" -#endif -#ifdef HAS_LAUNCHCFMAPP +#if defined(__APPLE__) +# define ResType MacResType +# include +# undef ResType +# if TARGET_CPU_PPC +# include "Classic.h" +# endif # include "Carbon.h" #endif #include "Executor.h" @@ -30,16 +33,22 @@ static vector launchMethods; static void RegisterLaunchMethods() { - launchMethods = { -#if defined(__APPLE__) && defined(__powerpc) - new Classic(), + vector methods = { +#if defined(__APPLE__) +# if TARGET_CPU_PPC + new Classic(), +# endif + new Carbon(), #endif -#ifdef HAS_LAUNCHCFMAPP - new Carbon(), -#endif - new Executor(), new MiniVMac() - // #### Add new `LaunchMethod`s here. - }; + new Executor(), new MiniVMac() + // #### Add new `LaunchMethod`s here. + }; + + for(LaunchMethod *m : methods) + { + if(m->CheckPlatform()) + launchMethods.push_back(m); + } } static void usage() @@ -59,7 +68,7 @@ static void usage() vector configuredMethods, unconfiguredMethods; for(LaunchMethod *method : launchMethods) (method->CheckOptions(options) ? configuredMethods : unconfiguredMethods) - .push_back(method->GetName()); + .push_back(method->GetName()); if(!configuredMethods.empty()) { @@ -79,10 +88,10 @@ static void usage() string e = options["emulator"].as(); std::cerr << "\nChosen emulator/environment: " << e; if(std::find(configuredMethods.begin(), configuredMethods.end(), e) - != configuredMethods.end()) + != configuredMethods.end()) std::cerr << "\n"; else if(std::find(unconfiguredMethods.begin(), unconfiguredMethods.end(), e) - != unconfiguredMethods.end()) + != unconfiguredMethods.end()) std::cerr << " (needs more configuration)\n"; else std::cerr << " (UNKNOWN)\n"; @@ -101,33 +110,33 @@ int main(int argc, char *argv[]) configFiles = { string(getenv("HOME")) + "/.LaunchAPPL.cfg", RETRO68_PREFIX "/LaunchAPPL.cfg"}; desc.add_options() - ("help,h", "show this help message") - ("list-emulators,l", "get the list of available, fully configured emulators/environments") - ("make-executable,x", po::value(), "make a MacBinary file executable") + ("help,h", "show this help message") + ("list-emulators,l", "get the list of available, fully configured emulators/environments") + ("make-executable,x", po::value(), "make a MacBinary file executable") ; po::options_description configdesc; configdesc.add_options() - ("emulator,e", po::value(), "what emulator/environment to use") + ("emulator,e", po::value(), "what emulator/environment to use") ; for(LaunchMethod *lm : launchMethods) lm->GetOptions(configdesc); desc.add(configdesc); desc.add_options() - ("timeout,t", po::value(),"abort after timeout") + ("timeout,t", po::value(),"abort after timeout") ; po::options_description hidden, alldesc; hidden.add_options() - ("application,a", po::value(), "application" ) + ("application,a", po::value(), "application" ) ; alldesc.add(desc).add(hidden); try { auto parsed = po::command_line_parser(argc, argv) - .options(alldesc) - .positional(po::positional_options_description().add("application", -1)) - .style(po::command_line_style::default_style) - .run(); + .options(alldesc) + .positional(po::positional_options_description().add("application", -1)) + .style(po::command_line_style::default_style) + .run(); po::store(parsed, options); } diff --git a/LaunchAPPL/LaunchMethod.cc b/LaunchAPPL/LaunchMethod.cc index b79928552c..dfe67cf980 100644 --- a/LaunchAPPL/LaunchMethod.cc +++ b/LaunchAPPL/LaunchMethod.cc @@ -18,6 +18,11 @@ void LaunchMethod::GetOptions(boost::program_options::options_description &desc) { } +bool LaunchMethod::CheckPlatform() +{ + return true; +} + bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options) { return true; diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h index 5fa1eb53f9..bcbf69742c 100644 --- a/LaunchAPPL/LaunchMethod.h +++ b/LaunchAPPL/LaunchMethod.h @@ -20,6 +20,8 @@ public: virtual std::string GetName() = 0; virtual void GetOptions(options_description& desc); + + virtual bool CheckPlatform(); virtual bool CheckOptions(variables_map& options); virtual std::unique_ptr MakeLauncher(variables_map& options) = 0; From fd5a1e57f2eabd184344475bac187893022437b8 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 15:32:12 +0200 Subject: [PATCH 25/29] build-toolchain.bash: add missing options to help message --- build-toolchain.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-toolchain.bash b/build-toolchain.bash index 339acb2acf..1ec3f849b4 100755 --- a/build-toolchain.bash +++ b/build-toolchain.bash @@ -42,6 +42,8 @@ function usage() echo " --no-ppc disable classic PowerPC CFM support" echo " --no-carbon disable Carbon CFM support" echo " --clean-after-build remove intermediate build files right after building" + echo " --host-cxx-compiler specify C++ compiler (needed on Mac OS X 10.4)" + echo " --host-c-compiler specify C compiler (needed on Mac OS X 10.4)" echo " --help show this help message" } From bcfcbb90a3f8233c50a2c4f286b6999710b3d26c Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 15:32:57 +0200 Subject: [PATCH 26/29] fix data forks in MacBinary --- ResourceFiles/ResourceFile.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ResourceFiles/ResourceFile.cc b/ResourceFiles/ResourceFile.cc index fb86aef9f4..4ce0dd1032 100644 --- a/ResourceFiles/ResourceFile.cc +++ b/ResourceFiles/ResourceFile.cc @@ -70,7 +70,9 @@ static void writeMacBinary(std::ostream& out, std::string filename, out.seekp(128); out << data; std::streampos dataend = out.tellp(); - std::streampos rsrcstart = ((int)dataend + 0x7F) & ~0x7F; + while((int)out.tellp() % 128) + byte(out,0); + std::streampos rsrcstart = out.tellp(); //((int)dataend + 0x7F) & ~0x7F; rsrc.writeFork(out); std::streampos rsrcend = out.tellp(); @@ -351,9 +353,11 @@ bool ResourceFile::read() unsigned short crc = CalculateCRC(0,header,124); if(word(in) != crc) return false; + in.seekg(128); std::vector buf(datasize); in.read(buf.data(), datasize); data = std::string(buf.begin(), buf.end()); + datasize = ((int)datasize + 0x7F) & ~0x7F; in.seekg(128 + datasize); resources = Resources(in); } From 24d9c906868e34a2cddaae93c25dc2ecba755052 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 15:33:50 +0200 Subject: [PATCH 27/29] Rez: create empty / data-only output file if output file name is explicitly given, but no input --- Rez/Rez.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Rez/Rez.cc b/Rez/Rez.cc index 2b123bd3ec..d186c3a6ee 100644 --- a/Rez/Rez.cc +++ b/Rez/Rez.cc @@ -79,7 +79,9 @@ int main(int argc, const char *argv[]) po::notify(options); - if(options.count("help") || (!options.count("input") && !options.count("copy"))) + if(options.count("help") + || (!options.count("input") && !options.count("copy") + && !options.count("output"))) { usage(); return 0; From 3223176f5b9491e48e58bafbb45f7fdc52f0138f Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Thu, 5 Oct 2017 15:47:24 +0200 Subject: [PATCH 28/29] fix previous commit --- LaunchAPPL/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index ad801680fa..04f5aa6f8d 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -9,10 +9,8 @@ if(APPLE) LIST(APPEND LAUNCHMETHODS Classic.h Classic.cc ) - if(LAUNCHCFMAPP) - LIST(APPEND LAUNCHMETHODS - Carbon.h Carbon.cc) - endif() + LIST(APPEND LAUNCHMETHODS + Carbon.h Carbon.cc) endif() add_definitions(-DRETRO68_PREFIX="${CMAKE_INSTALL_PREFIX}") From ed0b24a6ad74257d19e1c61cf260fa5f503cb710 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Sat, 7 Oct 2017 01:37:53 +0200 Subject: [PATCH 29/29] improve documentation --- LaunchAPPL/LaunchMethod.h | 45 +++++++++++++++++++++++++++++ LaunchAPPL/Launcher.h | 49 +++++++++++++++++++++++++++++-- README.md | 61 ++++++++++++++++++++++++++++++++------- 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h index bcbf69742c..c8606b4bc3 100644 --- a/LaunchAPPL/LaunchMethod.h +++ b/LaunchAPPL/LaunchMethod.h @@ -9,6 +9,12 @@ class Launcher; +/** + * @brief The LaunchMethod class + * + * To add a new backend to LaunchAPPL, start by subclassing this + * and updating RegisterLaunchMethods() in LaunchAPPL.cc. + */ class LaunchMethod { public: @@ -18,15 +24,54 @@ public: LaunchMethod(); virtual ~LaunchMethod(); + /** + * @brief GetName + * @return the name of the launch method, as it will be specified on the command line + */ virtual std::string GetName() = 0; + + /** + * @brief GetOptions + * @param desc + * + * Add any command line options for this LaunchMethod + * to the options_description structure. + */ virtual void GetOptions(options_description& desc); + /** + * @brief CheckPlatform + * + * Check whether this is the right kind of machine to use this method. + * For things like Apple's Classic Environment, which is only available on some system versions. + * The default implementation returns true. + */ virtual bool CheckPlatform(); + + /** + * @brief CheckOptions + * @param options + * @return are we ready to run? + * + * Check whether all necessary options have been specified. + * Don't output error messages here, this is also called when outputting usage information + */ virtual bool CheckOptions(variables_map& options); + /** + * @brief MakeLauncher + * @param options + * @return a new instance of a subclass of Launcher which will do the actual work + */ virtual std::unique_ptr MakeLauncher(variables_map& options) = 0; protected: + + /** + * @brief CheckExecutable + * @param program + * @return true if "program" exists in the $PATH and is executable. + */ bool CheckExecutable(std::string program); }; diff --git a/LaunchAPPL/Launcher.h b/LaunchAPPL/Launcher.h index e6bf703725..5c8a66fec8 100644 --- a/LaunchAPPL/Launcher.h +++ b/LaunchAPPL/Launcher.h @@ -5,6 +5,12 @@ #include "ResourceFile.h" #include +/** + * @brief The Launcher class + * + * Subclasses are instantiated by the corresponding LaunchMethod subclasses. + */ + class Launcher { protected: @@ -16,16 +22,55 @@ protected: int ChildProcess(std::string program, std::vector args, int timeout); public: + /** + * @brief Launcher + * @param options + * + * Create a Launcher object and set up a temporary directory to play in. + * Reads the Applicatio specified on the command line into the `app` member variable. + * Also create an empty file named 'out' in the temporary directory, for test suite programs. + */ Launcher(boost::program_options::variables_map& options); + + /** + * @brief Launcher + * @param options + * @param f + * + * Create a Launcher object, set up a temporary directory + * and store the application to be executed at `appPath` in the temporary directory, + * using format `f`. + */ Launcher(boost::program_options::variables_map& options, ResourceFile::Format f); - void KeepTempFiles() { keepTempFiles = true; } + /** + * @brief ~Launcher + * Delete our temporary directory. + */ + virtual ~Launcher(); + + /** + * @brief Go + * @param timeout + * @return true for success + * + * Launch the application, return true on success and false on error or timeout. + */ virtual bool Go(int timeout = 0) = 0; + /** + * @brief DumpOutput + * + * After the application has been run, copy the contents of the 'out' file to stdout. + */ virtual void DumpOutput(); - virtual ~Launcher(); + /** + * @brief KeepTempFiles + * Inhibit deletion of the temporary directory. + */ + void KeepTempFiles() { keepTempFiles = true; } }; #endif // LAUNCHER_H diff --git a/README.md b/README.md index 5325cbb324..d5f815e60b 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ of the Retro68 directory: ../Retro68/build-toolchain.bash The toolchain will be installed in the "toolchain" directory inside -the build directory. +the build directory. All the commands are in `toolchain/bin`, so you might want +to add that to your `PATH`. If you're building this on a PowerMac running Mac OS X 10.4, tell the build script to use the gcc you've installed via tigerbrew: @@ -100,7 +101,6 @@ The `build-host`, `build-target`, `build-target-ppc` and `build-target-carbon` directories are CMake build directories generated from the top-level `CMakeLists.txt`, so you can also `cd` to one of these and run `make` separately if you've made changes. - Sample programs --------------- @@ -203,15 +203,6 @@ using Rez. ### LaunchAPPL A tool for lauching compiled Mac applications via various emulators. -Currently, there are the following backends: - -* classic - launch in the Classic environment on PowerPC Macs up to Tiger (10.4) -* carbon - launch as a Carbon app on PowerPC Macs and via Rosetta on Intel Macs up to Snow Leopard (10.6) -* minivmac - launch using the Mini vMac emulator -* executor - launch using Executor - -**CONTRIBUTION OPPORTUNITY** - This tool can easily be extended with further backends, -so make it work with your favourtite emulator. ### ConvertObj @@ -295,3 +286,51 @@ The original parts of Retro68 are licensed under GPL3+, as are most other parts. Some parts are licensed GPL2+ or with more liberal licenses. Check the copyright notices in the individual files. + + + + +LaunchAPPL and the Test Suite +----------------------------- + +`LaunchAPPL` is a tool included with Retro68 intended to make launching the +compiled Mac applications easier. It's use is optional, so you may skip reading +this section. + +Currently, LaunchAPPL supports the following methods for launching Mac applications: + +* classic - launch in the Classic environment on PowerPC Macs up to Tiger (10.4) +* carbon - launch as a Carbon app on PowerPC Macs and via Rosetta on Intel Macs up to Snow Leopard (10.6) +* minivmac - launch using the Mini vMac emulator +* executor - launch using Executor + +If you're running on a Mac that's old enough to use the `classic` or `carbon` backends, +they will work out of the box, just launch an application as follows +(assuming you've added `Retro68-build/toolchain/bin` to your `PATH`): + + LaunchAPPL -e classic Retro68-build/build-target/Samples/Raytracer/Raytracer2.bin + LaunchAPPL -e carbon Retro68-build/build-target-carbon/Samples/Raytracer/Raytracer2.bin + +To specify either environment as a default, or to configure one of the other emulators, +copy the file `Retro68/LaunchAPPL/LaunchAPPL.cfg.example` to `~/.LaunchAPPL.cfg` +and edit to taste (documentation is provided in comments). + +**CONTRIBUTION OPPORTUNITY** - This tool can easily be extended with further backends, +so make it work with your favourtite emulator. Just add new subclasses for the +`LaunchMethod` and `Launcher` classes, they're documented. + +### The Test Suite + +The directory `AutomatedTests` contains an autonated test suite that runs via +`LaunchAPPL`. It's currently only relevant if you want to hack on the low-level +parts of Retro68. + +The test suite will be configured automatically on sufficiently old Macs. +Everywhere else, first configure `LaunchAPPL` (see above) and then: + + cs Retro68-build/build-target + cmake . -DRETRO68_LAUNCH_METHOD=minivmac # or executor, ... + make + +To run the tests, invoke `ctest` in the `build-target` directory. + ctest