diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index fe34f1587..a3e2ff387 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -36,6 +36,7 @@ jobs: make mingw-w64-i686-gcc mingw-w64-i686-lua + mingw-w64-i686-nsis ninja bison flex @@ -44,5 +45,12 @@ jobs: - uses: actions/checkout@v3 - name: build run: | - make - + make LDFLAGS="-s -static" CFLAGS=-Os + - name: package + run: | + make ack-setup.exe + - name: upload setup + uses: actions/upload-artifact@v2 + with: + name: ${{ github.event.repository.name }}.${{ github.sha }} + path: ack-setup.exe diff --git a/Makefile b/Makefile index 4db53e18e..3f4a66716 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,12 @@ ACK_TEMP_DIR ?= /tmp # install it and just want to run the ACK from the build directory # (/tmp/ack-build/staging, by default), leave this as $(INSDIR). +ifeq ($(OS),Windows_NT) +PREFIX ?= c:/ack +else PREFIX ?= /usr/local #PREFIX = $(INSDIR) +endif # Where do you want to put the object files used when building? @@ -119,12 +123,16 @@ $(build-file): first/ackbuilder.lua Makefile $(lua-files) INSDIR=$(INSDIR) \ PLATIND=$(PLATIND) \ PLATDEP=$(PLATDEP) \ - PREFIX=$(PREFIX) \ + PREFIX="$(PREFIX)" \ AR=$(AR) \ CC=$(CC) \ CFLAGS="$(CFLAGS)" \ + LDFLAGS="$(LDFLAGS)" \ > $(build-file) +ack-setup.exe: etc/windows-installer.nsi + makensis -dBUILDDIR=$(BUILDDIR)/staging -dOUTFILE="$$(realpath $@)" $< + install: mkdir -p $(PREFIX) tar cf - -C $(INSDIR) . | tar xvf - -C $(PREFIX) diff --git a/etc/windows-installer.nsi b/etc/windows-installer.nsi new file mode 100644 index 000000000..ecd0b69f8 --- /dev/null +++ b/etc/windows-installer.nsi @@ -0,0 +1,76 @@ +!include MUI2.nsh + +Name "The Amsterdam Compiler Kit" +OutFile "${OUTFILE}" +Unicode True + +InstallDir "c:\ack" + +RequestExecutionLevel admin +SetCompressor /solid lzma + +;-------------------------------- + +!define MUI_WELCOMEPAGE_TITLE "The Amsterdam Compiler Kit" +!define MUI_WELCOMEPAGE_TEXT "The ACK is a compiler toolchain supporting a \ + variety of frontend and backends. It's not easy to use, so if you don't \ + know what you're doing you shouldn't install this.$\r$\n\ + $\r$\n\ + This wizard will install the ACK on your computer.$\r$\n\ + $\r$\n\ + Please note that it doesn't support being installed into a path \ + with a space in it, and if you put it anywhere other than the default \ + then you'll need to set the ACKDIR environment variable to point to it \ + before use.$\r$\n\ + $\r$\n\ + $_CLICK" + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\nsis.bmp" +!define MUI_ABORTWARNING + +!insertmacro MUI_PAGE_WELCOME + +!define MUI_COMPONENTSPAGE_NODESC +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!define MUI_FINISHPAGE_TITLE "Installation complete" +!define MUI_FINISHPAGE_TEXT_LARGE +!define MUI_FINISHPAGE_TEXT "The ACK is now ready to use, but \ + hasn't been added to your path.$\r$\n\ + $\r$\n\ + Have fun!" + +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" + +; The stuff to install +Section "The ACK (required)" + SectionIn RO + SetOutPath $INSTDIR + File /r "${BUILDDIR}\*.*" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\WordGrinder" "DisplayName" "WordGrinder for Windows" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\WordGrinder" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\WordGrinder" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\WordGrinder" "NoRepair" 1 + WriteUninstaller "uninstall.exe" +SectionEnd + +;-------------------------------- + +; Uninstaller + +Section "Uninstall" + ; Remove registry keys + ; Remove files and uninstaller + RMDir /r $INSTDIR +SectionEnd diff --git a/first/build.lua b/first/build.lua index 20d834437..9fbc78080 100644 --- a/first/build.lua +++ b/first/build.lua @@ -225,7 +225,7 @@ definerule("cprogram", commands = { type="strings", default={ - "$(CC) -o %{outs[1]} %{ins} %{ins}" + "$(CC) $LDFLAGS -o %{outs[1]} %{ins} %{ins}" }, } }, diff --git a/h/build.lua b/h/build.lua index 4163e8249..93b1715d8 100644 --- a/h/build.lua +++ b/h/build.lua @@ -3,7 +3,7 @@ normalrule { ins = {}, outleaves = { "em_path.h" }, commands = { - "echo '#define EM_DIR \"$(PREFIX)\"' >> %{outs}", + "echo '#define EM_DIR \"$(PREFIX)\"' > %{outs}", "echo '#define ACK_PATH \"share/ack/descr\"' >> %{outs}", } } diff --git a/modules/src/system/syssystem.c b/modules/src/system/syssystem.c index c86b0be65..6013ade8d 100644 --- a/modules/src/system/syssystem.c +++ b/modules/src/system/syssystem.c @@ -2,34 +2,79 @@ #include #include "system.h" +#if defined WIN32 + #define ESCAPECHAR '^' + + static int needs_escaping(char c) + { + switch (c) + { + case ' ': + case '"': + case '\'': + case '(': + case ')': + case '^': + return 1; + + default: + return 0; + } + } +#else + #define ESCAPECHAR '\\' + + static int needs_escaping(char c) + { + switch (c) + { + case ' ': + case '"': + case '\'': + case '\\': + case '(': + case ')': + return 1; + + default: + return 0; + } + } +#endif + +static char* append_escaped(char* buffer, const char* word) +{ + for (;;) + { + char c = *word++; + if (!c) + break; + if (needs_escaping(c)) + *buffer++ = ESCAPECHAR; + *buffer++ = c; + } + return buffer; +} + int sys_system(const char* prog, const char* const* arglist) { /* Calculate the maximum length of the command line. */ - int len = strlen(prog); + int len = strlen(prog) * 2 + 1; for (const char* const* arg = arglist+1; *arg; arg++) len += strlen(*arg) * 2 + 1; /* Now actually build the command line. */ char* cmdline = malloc(len + 1); - strcpy(cmdline, prog); - char* p = cmdline + strlen(prog); + char* p = append_escaped(cmdline, prog); for (const char* const* arg = arglist+1; *arg; arg++) { const char* word = *arg; *p++ = ' '; - for (;;) - { - char c = *word++; - if (!c) - break; - if ((c == ' ') || (c == '\"') || (c == '\'')) - *p++ = '\\'; - *p++ = c; - } + p = append_escaped(p, word); } *p = 0;