diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index 5e1ffff..d4331ab 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -60,6 +60,14 @@ add_custom_command( DEPENDS debugger.h template_parser.h ) +add_custom_command( + OUTPUT mpw-make-parse.cpp + COMMAND ragel -p -G2 -o mpw-make-parse.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-make-parse.rl" + MAIN_DEPENDENCY mpw-make-parse.rl + DEPENDS debugger.h +) + + set_source_files_properties( loadtrap.cpp lexer.cpp template_loader.cpp @@ -85,3 +93,7 @@ set_target_properties(mpw PROPERTIES LINK_FLAGS "-ledit") add_executable(disasm disasm.cpp) target_link_libraries(disasm CPU_LIB) target_link_libraries(disasm MACOS_LIB) + + +add_executable(mpw-make mpw-make-parse.cpp mpw-make.cpp) + diff --git a/bin/mpw-make-parse.rl b/bin/mpw-make-parse.rl new file mode 100644 index 0000000..d8f4215 --- /dev/null +++ b/bin/mpw-make-parse.rl @@ -0,0 +1,171 @@ + +/* + * ragel code to extract commands from mpw make output + */ + +#include +#include +#include +#include +#include + +#include +#include + + /* + +PascalIIGS IRModule.p +asmiigs TheHeader.aii + +reziigs IRModule.r -o IRModule + +LinkIIGS TheHeader.aii.obj IRModule.p.obj ? + -lib "{PIIGSLibraries}"PLib ? + -o IRModule ? + -t $BC -at $4001 + DuplicateIIgs -y -m IRModule : + +*/ + +%%{ + machine make; + alphtype unsigned char; + + main := |* + + 0xb6 '\n' => { + // line continuation marker. + // terminate the current token? + }; + + + '\n' => { + // terminate the command. + if (!token.empty()) { + argv.push_back(strdup(token.c_str())); + token.clear(); + } + if (argv.size() > 1) { + int ok = launch_command(argv); + if (ok != 0) { + fprintf(stderr, "command failed with exit code %d\n", ok); + return ok; + } + } + + for (auto cp : argv) { free(cp); } + argv.clear(); + argv.push_back(strdup("mpw")); + }; + + [ \t]+ => { + // token separator. + if (!token.empty()) { + argv.push_back(strdup(token.c_str())); + token.clear(); + } + }; + + '"{' [A-Za-z][A-Za-z0-9]* '}"' => { + // environment variable. remove quotes. + token.append(ts + 1, te - 1); + }; + + any => { + token.push_back(*ts); + }; + + *|; +}%% + +namespace { + + %% write data nofinal; + +} + +extern int launch_command(std::vector &argv); + +int parse_makefile(int fd) { + + std::string token; + std::vector argv; + + unsigned char buffer[4096]; + + const unsigned char *ts; + const unsigned char *te; + const unsigned char *p; + const unsigned char *pe; + const unsigned char *eof; + + int cs, act; + + unsigned offset = 0; + + + %% write init; + + // strdup so it can be free() later. + argv.push_back(strdup("mpw")); + + + bool done = false; + while (!done) { + ssize_t s; + s = read(fd, buffer + offset, sizeof(buffer) - offset); + if (s < 0) { + if (errno == EINTR) continue; + perror("read: "); + exit(EX_OSERR); + } + + p = buffer + offset; + pe = p + s; + if (s == 0) { + done = true; + eof = pe; + } + + %% write exec; + + if (cs == make_error) { + fprintf(stderr, "lexer error\n"); + exit(EX_DATAERR); + } + // if inside a token, shift data around. + + if (ts == 0) { + offset = 0; + } else { + offset = pe - ts; + memmove(buffer, ts, offset); + + te = buffer + (te - ts); + ts = buffer; + + if (offset == sizeof(buffer)) { + fprintf(stderr, "buffer exceeded\n"); + exit(EX_SOFTWARE); + } + + } + + + } + // any remaining argv? + if (argv.size() > 1) { + fprintf(stderr, "warning: unterminated line\n"); + + int ok = launch_command(argv); + if (ok != 0) { + fprintf(stderr, "command failed with exit code %d\n", ok); + return ok; + } + } + + for (auto cp : argv) { free(cp); } + argv.clear(); + + return 0; +} diff --git a/bin/mpw-make.cpp b/bin/mpw-make.cpp new file mode 100644 index 0000000..a82007a --- /dev/null +++ b/bin/mpw-make.cpp @@ -0,0 +1,231 @@ +/* + * + * Run MPW's make. + * + * MPW Make prints a list of commands to stdout. + * read and execute them. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + + +int parse_makefile(int fd); +void help(void); +void launch_make(std::vector &argv); +void print_command(const std::vector &argv); +int launch_command(std::vector &argv); + +bool dry_run = false; + +void help(void) { + + #undef _ + #define _(x) puts(x) + + _("Make # build up-to-date version of a program"); + _("Make [option...] [target...]"); + _(" -d name[=value] # define variable name (overrides makefile definition)"); + _(" -e # rebuild everything regardless of dates"); + _(" -f filename # read dependencies from specified file (default: MakeFile)"); + _(" -i dirname # additional directory to search for include files"); + _(" -[no]mf # [don't] use temporary memory (default: mf)"); + _(" -p # write progress information to diagnostics"); + _(" -r # display the roots of the dependency graph"); + _(" -s # display the structure of the dependency graph"); + _(" -t # touch dates of targets and prerequisites"); + _(" -u # write list of unreachable targets to diagnostics"); + _(" -v # write verbose explanations to diagnostics (implies -p)"); + _(" -w # suppress warning messages"); + _(" -y # like -v, but omit announcing up-to-date targets"); + _(""); + _(" --help # display help"); + _(" --dry-run # show what commands would run"); +#undef _ +} + + +void launch_make(std::vector &argv) { + int ok; + + if (argv.empty() || argv.back() != nullptr) + argv.push_back(nullptr); + + ok = execvp(argv.front(), argv.data()); + perror("execvp: "); + exit(EX_OSERR); +} + +void print_command(const std::vector &argv) { + + for (auto cp : argv) { + if (cp) printf("%s ", cp); + } + printf("\n"); +} + +int launch_command(std::vector &argv) { + + int pid; + + // parse command-line + // launch command. + // wait for a return status. + //puts(cmd.c_str()); + + if (argv.empty() || argv.back() != nullptr) + argv.push_back(nullptr); + + print_command(argv); + if (dry_run) return 0; + + pid = fork(); + if (pid < 0) { + perror("fork: "); + exit(EX_OSERR); + } + + if (pid == 0) { + execvp(argv.front(), argv.data()); + perror("execvp: "); + exit(EX_OSERR); + } + + for(;;) { + int status; + pid_t ok; + ok = waitpid(pid, &status, 0); + if (ok < 0) { + if (errno == EINTR) continue; + perror("waitpid:"); + exit(EX_OSERR); + } + printf("\n"); + + if (WIFEXITED(status)) return WEXITSTATUS(status); + if (WIFSIGNALED(status)) return -1; + fprintf(stderr, "waitpid - unexpected result\n"); + exit(EX_OSERR); + } + +} + + + +int main(int argc, char **argv) { + + int pipes[2]; + int ok; + int child; + + + std::vectornew_argv; + new_argv.push_back((char *)"mpw"); + new_argv.push_back((char *)"Make"); + + //new_argv.insert(new_argv.end(), argv + 1, argv + argc); + + // filter out --help and --dry-run + + std::copy_if(argv + 1, argv + argc, std::back_inserter(new_argv), + [](char *cp){ + if (strcmp(cp, "--help") == 0) { + help(); + exit(0); + return false; + } + if (strcmp(cp, "--dry-run") == 0) { + dry_run = true; + return false; + } + return true; + } + + ); + + + ok = pipe(pipes); + if (ok < 0) { + perror("pipe: "); + } + + child = fork(); + if (child < 0) { + perror("fork: "); + exit(EX_OSERR); + } + + if (child == 0) { + // child. + //close(STDIN_FILENO); + //close(STDOUT_FILENO); + //close(STDERR_FILENO); + + ok = dup2(pipes[1], STDOUT_FILENO); + if (ok < 0) { + perror("dup2: "); + exit(EX_OSERR); + } + close(pipes[0]); + close(pipes[1]); + + launch_make(new_argv); // no return. + exit(0); + } + + // parent process. + // read commands from pipe[0] and execute them. + // 0xb6 is a line continuation marker. + //int fd = pipes[0]; + + /* + FILE *f = fdopen(pipes[0], "r"); + if (!f) { + perror("fdopen: "); + exit(EX_OSERR); + } + */ + close(pipes[1]); + + // prevent children from inheriting the pipe fd. + fcntl(pipes[0], F_SETFD, FD_CLOEXEC); + + ok = parse_makefile(pipes[0]); + close(pipes[0]); + + + // return any error from make itself. + for(;;) { + int status; + int ok = waitpid(child, &status, 0); + if (ok < 0) { + if (errno == EINTR) continue; + perror("waitpid: "); + exit(EX_OSERR); + } + + if (WIFEXITED(status)) { + ok = WEXITSTATUS(status); + if (ok) exit(ok); + break; + } + if (WIFSIGNALED(status)) + exit(1); + + fprintf(stderr, "waitpid - unexpected result\n"); + exit(EX_OSERR); + } + + return ok ? 2 : 0; +}