From 90cf5887ab6733440254907cbdfb231ec58c3d91 Mon Sep 17 00:00:00 2001 From: Jan Giesenberg Date: Sun, 17 May 2026 12:31:06 +0200 Subject: [PATCH 1/5] Fix Linux build with modern GCC --- README.md | 2 +- src/console/defrag.c | 3 ++- src/dll/udefrag/udefrag-internals.h | 3 +++ src/include/linux.h | 13 +++++++++++++ src/wincalls/curses.c | 1 + src/wincalls/ntfs-3g.c | 1 + src/wincalls/ntfs-3g.h | 1 + src/wincalls/wincalls.c | 25 +++++++++++++++++++++---- 8 files changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9db62de..3b33f93 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The results at the time were clear, no changes just a GUI on top. ```sh cd src -CFLAGS='-Wno-implict-function-declaration' make +make ``` ## Support diff --git a/src/console/defrag.c b/src/console/defrag.c index 963613b..7cbee6b 100644 --- a/src/console/defrag.c +++ b/src/console/defrag.c @@ -30,6 +30,7 @@ #ifdef LINUX #include #include +#include #endif #include "extrawin.h" @@ -730,7 +731,7 @@ static int process_volumes(void) #if STSC f = open(path->path,O_RDONLY); #else - f = open64(path->path,O_RDONLY); + f = open(path->path,O_RDONLY); #endif if (f > 0) { n = read(f,buf,512); diff --git a/src/dll/udefrag/udefrag-internals.h b/src/dll/udefrag/udefrag-internals.h index dd4161b..0034b5e 100644 --- a/src/dll/udefrag/udefrag-internals.h +++ b/src/dll/udefrag/udefrag-internals.h @@ -267,6 +267,9 @@ void stop_timing(char *operation_name,ULONGLONG start_time,udefrag_job_parameter int check_region(udefrag_job_parameters *jp,ULONGLONG lcn,ULONGLONG length); winx_blockmap *get_first_block_of_cluster_chain(winx_file_info *f,ULONGLONG vcn); +#ifdef LINUXMODE +void deliver_progress_info(udefrag_job_parameters *jp,int completion_status); +#endif NTSTATUS udefrag_fopen(winx_file_info *f,HANDLE *phFile); int is_file_locked(winx_file_info *f,udefrag_job_parameters *jp); int is_mft(winx_file_info *f,udefrag_job_parameters *jp); diff --git a/src/include/linux.h b/src/include/linux.h index 1648ce3..174d4d1 100644 --- a/src/include/linux.h +++ b/src/include/linux.h @@ -258,11 +258,15 @@ DWORD WINAPI GetEnvironmentVariableW(const utf_t*, utf_t*, DWORD); BOOLEAN WINAPI SetEnvironmentVariableA(const char*, const char*); BOOLEAN WINAPI SetEnvironmentVariableW(const utf_t*, const utf_t*); DWORD WINAPI GetLastError(void); +DWORD WINAPI FormatMessage(DWORD, LPCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); +DWORD WINAPI FormatMessageA(DWORD, LPCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); +LPVOID LocalFree(LPVOID); VOID WINAPI RtlZeroMemory(VOID*, SIZE_T); /* not WINAPI according to msdn */ BOOLEAN WINAPI SetConsoleCursorPosition(HANDLE, COORD); BOOLEAN WINAPI SetConsoleTextAttribute(HANDLE, WORD); BOOLEAN WINAPI GetConsoleScreenBufferInfo(HANDLE, CONSOLE_SCREEN_BUFFER_INFO*); BOOLEAN WINAPI SetConsoleWindowInfo(HANDLE, BOOLEAN, const SMALL_RECT*); +BOOLEAN WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE, BOOLEAN); DWORD MAKELANGID(DWORD, DWORD); /* * Extended C library for processing utf16le strings @@ -287,6 +291,8 @@ char *_strupr(char*); char *_strlwr(char*); int _vsnprintf(char*, size_t sz, const char*, va_list); +int _snprintf(char*, int, const char*, ...); +utf_t *_wcsupr(utf_t*); int _snwprintf(utf_t*, size_t, const utf_t*, ...); #if WNSC | STSC | SPGC int safe_fprintf(struct _iobuf*, const char*, ...); @@ -296,6 +302,12 @@ int safe_fprintf(struct _IO_FILE*, const char*, ...); void safe_dump(struct _IO_FILE*, const char*, const char*, int); #endif const char *calledfrom(void*); +void WgxDbgPrintLastError(const char*, ...); +void display_error(char*); +int ntfs_mounted_device(const char*); +int _getch(void); +int winx_bytes_to_hr(ULONGLONG, int, char*, int); +void winx_unload_library(void); /* * Miscellaneous @@ -308,6 +320,7 @@ const char *calledfrom(void*); void stop_there(int, const char*, int); #define get_out(n) stop_there(n,__FILE__,__LINE__) void initwincalls(void); +void endwincalls(void); /* * Memory allocation through libntfs-3g diff --git a/src/wincalls/curses.c b/src/wincalls/curses.c index 18be20b..c528ddc 100644 --- a/src/wincalls/curses.c +++ b/src/wincalls/curses.c @@ -36,6 +36,7 @@ #define trace stdtrace /* symbol conflict with curses */ #include +#include #undef trace #ifndef UNTHREADED diff --git a/src/wincalls/ntfs-3g.c b/src/wincalls/ntfs-3g.c index c2031a3..b73e35b 100644 --- a/src/wincalls/ntfs-3g.c +++ b/src/wincalls/ntfs-3g.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include diff --git a/src/wincalls/ntfs-3g.h b/src/wincalls/ntfs-3g.h index ca69669..9a86f56 100644 --- a/src/wincalls/ntfs-3g.h +++ b/src/wincalls/ntfs-3g.h @@ -25,6 +25,7 @@ */ HANDLE ntfs_open(const utf_t*); +int ntfs_mounted_device(const char*); int ntfs_close(HANDLE); int ntfs_unlink(HANDLE, const char*); int ntfs_sync(HANDLE); diff --git a/src/wincalls/wincalls.c b/src/wincalls/wincalls.c index f92bbfb..cb77e67 100644 --- a/src/wincalls/wincalls.c +++ b/src/wincalls/wincalls.c @@ -39,6 +39,7 @@ #include //#include #include +#include #include #include #include @@ -50,6 +51,11 @@ #include "ntfs-3g.h" #include "extrawin.h" +#ifdef CURSES +int curs_set(int); +int endwin(void); +#endif + #if STSC #undef USETIMEOFDAY #else @@ -1197,7 +1203,11 @@ fprintf(stderr,"** Heap was not allocated\n"); NTSTATUS NTAPI NtAllocateVirtualMemory(HANDLE a, PVOID *b, SIZE_T c, SIZE_T *d, SIZE_T e, SIZE_T f) RUNDEF(NtAllocateVirtualMemory) -UNDEF(LocalFree) +LPVOID LocalFree(LPVOID p) +{ + free(p); + return (NULL); +} NTSTATUS NTAPI NtFreeVirtualMemory(HANDLE a, PVOID *b, SIZE_T *c, SIZE_T d) RUNDEF(NtFreeVirtualMemory) @@ -2136,7 +2146,14 @@ RUNDEF(SetConsoleWindowInfo) UNDEF(udefrag_fbsize) UNDEF(udefrag_toupper) -UNDEF(_wcsupr) +utf_t *_wcsupr(utf_t *s) +{ + utf_t *p; + + for (p = s; p && *p; p++) + *p = winx_toupper(*p); + return (s); +} utf16_t * WINAPI GetCommandLineW(void) { @@ -2298,14 +2315,14 @@ DWORD WINAPI FormatMessageA(DWORD flg, LPCVOID src, DWORD idmess, DWORD idlang, LPSTR buf, DWORD sz, va_list *args) //DWORD WINAPI FormatMessageA(long, char*, long, long, char*, long, va_list*) { - return (-1); /* return an error */ + return (0); /* return an error */ } DWORD WINAPI FormatMessage(DWORD flg, LPCVOID src, DWORD idmess, DWORD idlang, LPSTR buf, DWORD sz, va_list *args) //DWORD WINAPI FormatMessage(long, char*, long, long, char*, long, va_list*) { - return (-1); /* return an error */ + return (0); /* return an error */ } NTSTATUS NTAPI xNtCreateFile(PHANDLE ph, ACCESS_MASK acc, POBJECT_ATTRIBUTES p, From 73e802337376c489eb712abdbb20e0adad60866e Mon Sep 17 00:00:00 2001 From: Jan Giesenberg Date: Sun, 17 May 2026 12:50:25 +0200 Subject: [PATCH 2/5] ci: added basic build pipeline --- .github/workflows/build.yml | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..75d043c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,44 @@ +name: Build and test + +on: + push: + branches: + - develop + - main + - master + pull_request: + +jobs: + linux: + name: Linux build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + lsb-release \ + libncurses-dev \ + ntfs-3g-dev + + - name: Build + working-directory: src + run: make CFLAGS=-Wno-implicit-function-declaration + + - name: Test help output + working-directory: src + run: | + test -x ./udefrag + ./udefrag --help | grep -q "Usage: udefrag" + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: udefrag-linux + path: src/udefrag + if-no-files-found: error From a974517df798ebb3d425ad506b31c501557dd458 Mon Sep 17 00:00:00 2001 From: Jan Giesenberg Date: Sun, 17 May 2026 13:05:05 +0200 Subject: [PATCH 3/5] feat: add autotools based build --- .github/workflows/build.yml | 20 ++++--- .gitignore | 109 +++++++++++++++++++++++++++++++++++- Makefile.am | 80 ++++++++++++++++++++++++++ README.md | 4 +- autogen.sh | 4 ++ configure.ac | 57 +++++++++++++++++++ 6 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75d043c..c38f3e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,24 +21,28 @@ jobs: run: | sudo apt-get update sudo apt-get install -y \ + autoconf \ + automake \ build-essential \ lsb-release \ libncurses-dev \ ntfs-3g-dev + - name: Bootstrap + run: ./autogen.sh + + - name: Configure + run: ./configure + - name: Build - working-directory: src - run: make CFLAGS=-Wno-implicit-function-declaration + run: make - - name: Test help output - working-directory: src - run: | - test -x ./udefrag - ./udefrag --help | grep -q "Usage: udefrag" + - name: Test + run: make check - name: Upload binary uses: actions/upload-artifact@v4 with: name: udefrag-linux - path: src/udefrag + path: udefrag if-no-files-found: error diff --git a/.gitignore b/.gitignore index 00f67fb..9e81463 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,61 @@ -# Created by https://www.gitignore.io/api/c,c++,linux,macos,windows -# Edit at https://www.gitignore.io/?templates=c,c++,linux,macos,windows +# Created by https://www.toptal.com/developers/gitignore/api/c,c++,linux,macos,windows,git,autotools,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,linux,macos,windows,git,autotools,visualstudiocode + +### Autotools ### +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.cache +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + +### Autotools Patch ### ### C ### # Prerequisites @@ -74,6 +130,21 @@ dkms.conf # Executables +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + ### Linux ### *~ @@ -98,6 +169,7 @@ dkms.conf # Icon must end with two \r Icon + # Thumbnails ._* @@ -117,6 +189,29 @@ Network Trash Folder Temporary Items .apdisk +### macOS Patch ### +# iCloud generated files +*.icloud + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + ### Windows ### # Windows thumbnail cache files Thumbs.db @@ -143,8 +238,16 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.gitignore.io/api/c,c++,linux,macos,windows +# End of https://www.toptal.com/developers/gitignore/api/c,c++,linux,macos,windows,git,autotools,visualstudiocode .git udefrag + +# Autotools generated files +/autotools-config.h +/autotools-config.h.in +/ultradefrag4linux-*/ +/ultradefrag4linux-*.tar.* +/.deps/ +**/.deps/ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b54b903 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,80 @@ +AUTOMAKE_OPTIONS = foreign subdir-objects + +AM_CPPFLAGS = \ + -DLXGC=1 \ + $(NTFS3G_CFLAGS) \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/console \ + -I$(top_srcdir)/src/dll/udefrag \ + -I$(top_srcdir)/src/dll/zenwinx \ + -I$(top_srcdir)/src/wincalls + +bin_PROGRAMS = udefrag + +noinst_HEADERS = \ + src/console/udefrag.h \ + src/include/compiler.h \ + src/include/extrawin.h \ + src/include/linux.h \ + src/include/ultradfgver.h \ + src/include/version.h \ + src/share/getopt.h \ + src/dll/udefrag/udefrag.h \ + src/dll/udefrag/udefrag-internals.h \ + src/dll/zenwinx/myendians.h \ + src/dll/zenwinx/mytypes.h \ + src/dll/zenwinx/ntndk.h \ + src/dll/zenwinx/ntfs.h \ + src/dll/zenwinx/partition.h \ + src/dll/zenwinx/prb.h \ + src/dll/zenwinx/zenwinx.h \ + src/dll/zenwinx/zenwinxver.h \ + src/wincalls/ntfs-3g.h + +EXTRA_DIST = autogen.sh + +udefrag_SOURCES = \ + src/console/defrag.c \ + src/console/map.c \ + src/console/options.c \ + src/dll/udefrag/analyze.c \ + src/dll/udefrag/defrag.c \ + src/dll/udefrag/map.c \ + src/dll/udefrag/move.c \ + src/dll/udefrag/optimize.c \ + src/dll/udefrag/options.c \ + src/dll/udefrag/reports.c \ + src/dll/udefrag/search.c \ + src/dll/udefrag/tasks.c \ + src/dll/udefrag/udefrag.c \ + src/dll/udefrag/volume.c \ + src/dll/zenwinx/dbg.c \ + src/dll/zenwinx/env.c \ + src/dll/zenwinx/event.c \ + src/dll/zenwinx/file.c \ + src/dll/zenwinx/ftw.c \ + src/dll/zenwinx/ftw_ntfs.c \ + src/dll/zenwinx/ldr.c \ + src/dll/zenwinx/list.c \ + src/dll/zenwinx/lock.c \ + src/dll/zenwinx/mem.c \ + src/dll/zenwinx/misc.c \ + src/dll/zenwinx/path.c \ + src/dll/zenwinx/prb.c \ + src/dll/zenwinx/string.c \ + src/dll/zenwinx/thread.c \ + src/dll/zenwinx/time.c \ + src/dll/zenwinx/volume.c \ + src/dll/zenwinx/zenwinx.c \ + src/wincalls/wincalls.c \ + src/wincalls/ntfs-3g.c \ + src/wincalls/curses.c + +udefrag_LDADD = \ + $(NTFS3G_LIBS) \ + $(PTHREAD_LIBS) \ + $(CURSES_LIBS) \ + -lm + +check-local: udefrag + ./udefrag --help | $(GREP) -q "Usage: udefrag" diff --git a/README.md b/README.md index 3b33f93..6ffb9ad 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,10 @@ The results at the time were clear, no changes just a GUI on top. ## Compiling ```sh -cd src +./autogen.sh +./configure make +make check ``` ## Support diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..1dd5220 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu + +autoreconf -fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..89b95e8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,57 @@ +AC_INIT([UltraDefrag4Linux], [5.0.0AB.8], [https://github.com/749/UltraDefrag4Linux/issues]) +AM_INIT_AUTOMAKE([foreign subdir-objects no-define]) +AC_CONFIG_SRCDIR([src/console/defrag.c]) + +AC_PROG_CC +AC_PROG_GREP +AC_PROG_RANLIB +AM_PROG_AR + +AC_ARG_WITH( + [ntfs3g-includedir], + [AS_HELP_STRING([--with-ntfs3g-includedir=DIR], [directory containing ntfs-3g headers])], + [ntfs3g_includedir="$withval"], + [ntfs3g_includedir=""]) + +AS_IF([test -z "$ntfs3g_includedir" && test -d /usr/include/ntfs-3g], + [ntfs3g_includedir="/usr/include/ntfs-3g"]) + +AS_IF([test -n "$ntfs3g_includedir"], + [NTFS3G_CFLAGS="-I$ntfs3g_includedir"], + [NTFS3G_CFLAGS=""]) + +AS_IF([test -z "$ntfs3g_includedir" || test ! -f "$ntfs3g_includedir/volume.h"], + [AC_MSG_ERROR([required ntfs-3g headers were not found; install ntfs-3g-dev or use --with-ntfs3g-includedir])]) + +AC_ARG_WITH( + [ntfs3g-libdir], + [AS_HELP_STRING([--with-ntfs3g-libdir=DIR], [directory containing libntfs-3g])], + [NTFS3G_LDFLAGS="-L$withval"], + [AS_IF([test -d /usr/lib/x86_64-linux-gnu], + [NTFS3G_LDFLAGS="-L/usr/lib/x86_64-linux-gnu"], + [NTFS3G_LDFLAGS=""])]) + +save_LDFLAGS=$LDFLAGS +LDFLAGS="$LDFLAGS $NTFS3G_LDFLAGS" +AC_CHECK_LIB([ntfs-3g], [ntfs_mount], + [NTFS3G_LIBS="$NTFS3G_LDFLAGS -lntfs-3g"], + [AC_MSG_ERROR([required ntfs-3g library was not found])]) +LDFLAGS=$save_LDFLAGS + +AC_CHECK_LIB([pthread], [pthread_create], + [PTHREAD_LIBS="-lpthread"], + [AC_MSG_ERROR([required pthread library was not found])]) + +AC_CHECK_LIB([ncurses], [initscr], + [CURSES_LIBS="-lncurses"], + [AC_CHECK_LIB([curses], [initscr], + [CURSES_LIBS="-lcurses"], + [AC_MSG_ERROR([required curses/ncurses library was not found])])]) + +AC_SUBST([NTFS3G_CFLAGS]) +AC_SUBST([NTFS3G_LIBS]) +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([CURSES_LIBS]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT From 38cdcd2c2597f6b59237566cfba2778a484bc528 Mon Sep 17 00:00:00 2001 From: Jan Giesenberg Date: Sun, 17 May 2026 13:53:53 +0200 Subject: [PATCH 4/5] ci: Add native Windows and fragmented volume CI --- .github/workflows/build.yml | 85 ++++- .gitignore | 8 + .../ci_fragmented_volume_test.ps1 | 301 ++++++++++++++++++ .../ci_fragmented_volume_test.sh | 126 ++++++++ ci/build-windows-native.sh | 49 +++ src/console/defrag.c | 14 +- src/tools/mkmod.lua | 90 +++--- 7 files changed, 619 insertions(+), 54 deletions(-) create mode 100644 TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.ps1 create mode 100755 TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.sh create mode 100755 ci/build-windows-native.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c38f3e9..b33fd1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,8 +9,8 @@ on: pull_request: jobs: - linux: - name: Linux build + ubuntu: + name: Ubuntu build runs-on: ubuntu-latest steps: @@ -26,6 +26,7 @@ jobs: build-essential \ lsb-release \ libncurses-dev \ + ntfs-3g \ ntfs-3g-dev - name: Bootstrap @@ -40,9 +41,89 @@ jobs: - name: Test run: make check + - name: Test fragmented NTFS image + run: sudo ./TestSuite/Create\ Fragmented\ Volumes/ci_fragmented_volume_test.sh ./udefrag + - name: Upload binary uses: actions/upload-artifact@v4 with: name: udefrag-linux path: udefrag if-no-files-found: error + + cachyos: + name: CachyOS build + runs-on: ubuntu-latest + container: + image: cachyos/cachyos:latest + + steps: + - name: Install dependencies + run: | + pacman -Syu --noconfirm + pacman -S --noconfirm --needed \ + autoconf \ + automake \ + base-devel \ + grep \ + ncurses \ + ntfs-3g + + - name: Checkout + uses: actions/checkout@v4 + + - name: Bootstrap + run: ./autogen.sh + + - name: Configure + run: ./configure + + - name: Build + run: make + + - name: Test + run: make check + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: udefrag-cachyos + path: udefrag + if-no-files-found: error + + windows: + name: Windows native build + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + base-devel + make + mingw-w64-ucrt-x86_64-binutils + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-lua + + - name: Build native Windows binary + shell: msys2 {0} + run: ./ci/build-windows-native.sh + + - name: Test fragmented NTFS volume + shell: pwsh + run: | + $env:PATH = "C:\msys64\ucrt64\bin;$env:PATH" + & ".\TestSuite\Create Fragmented Volumes\ci_fragmented_volume_test.ps1" + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: udefrag-windows-x64 + path: src/bin/amd64/* + if-no-files-found: error diff --git a/.gitignore b/.gitignore index 9e81463..4e05708 100644 --- a/.gitignore +++ b/.gitignore @@ -251,3 +251,11 @@ udefrag /ultradefrag4linux-*.tar.* /.deps/ **/.deps/ + +# Legacy Windows/MinGW build helpers and generated files +/ci/bin/ +Makefile.mingw +Makefile_x64.mingw +/src/console/getopt.c +/src/console/getopt.h +/src/console/getopt1.c diff --git a/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.ps1 b/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.ps1 new file mode 100644 index 0000000..014fbdf --- /dev/null +++ b/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.ps1 @@ -0,0 +1,301 @@ +param( + [string]$BinaryPath = "src\bin\amd64\udefrag.exe", + [string]$DriveLetter = "T", + [int]$VolumeSizeMB = 1024, + [int]$FillerFileCount = 300, + [int]$FillerFileSizeMB = 3, + [int]$FragmentedFileCount = 40, + [int]$FragmentedFileSizeMB = 5, + [string[]]$FileSystems = @( + "FAT", + "FAT32", + "exFAT", + "NTFS", + "NTFS_COMPRESSED", + "NTFS_MIXED", + "UDF_102", + "UDF_150", + "UDF_200", + "UDF_201", + "UDF_250", + "UDF_250_DUP" + ), + [switch]$FullFormat +) + +$ErrorActionPreference = "Stop" + +function Write-Step { + param([string]$Message) + Write-Host "==> $Message" +} + +function Invoke-Checked { + param( + [string]$FilePath, + [string[]]$ArgumentList + ) + + Write-Host "+ $FilePath $($ArgumentList -join ' ')" + & $FilePath @ArgumentList + if ($LASTEXITCODE -ne 0) { + throw "$FilePath exited with code $LASTEXITCODE" + } +} + +function Invoke-DiskPart { + param([string[]]$Commands) + + $scriptPath = Join-Path $env:TEMP ("udefrag-diskpart-{0}.txt" -f ([guid]::NewGuid())) + try { + $Commands | Set-Content -Path $scriptPath -Encoding ASCII + Invoke-Checked -FilePath "diskpart.exe" -ArgumentList @("/s", $scriptPath) + } finally { + Remove-Item -Path $scriptPath -Force -ErrorAction SilentlyContinue + } +} + +function Test-Administrator { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = [Security.Principal.WindowsPrincipal]::new($identity) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Resolve-TestDriveLetter { + param([string]$PreferredDriveLetter) + + $preferred = $PreferredDriveLetter.TrimEnd(":") + if (-not (Test-Path "$preferred`:\")) { + return $preferred + } + + foreach ($candidate in @("T", "U", "V", "W", "X", "Y", "Z")) { + if (-not (Test-Path "$candidate`:\")) { + return $candidate + } + } + + throw "No free drive letter is available for the fragmented volume test." +} + +function Get-TestFileSystem { + param([string]$Name) + + switch ($Name.ToUpperInvariant()) { + "FAT" { + return [pscustomobject]@{ Name = "FAT"; FormatFs = "FAT"; UdfRevision = $null; UdfDuplicate = $false; Compression = "none" } + } + "FAT32" { + return [pscustomobject]@{ Name = "FAT32"; FormatFs = "FAT32"; UdfRevision = $null; UdfDuplicate = $false; Compression = "none" } + } + "EXFAT" { + return [pscustomobject]@{ Name = "exFAT"; FormatFs = "exFAT"; UdfRevision = $null; UdfDuplicate = $false; Compression = "none" } + } + "NTFS" { + return [pscustomobject]@{ Name = "NTFS"; FormatFs = "NTFS"; UdfRevision = $null; UdfDuplicate = $false; Compression = "none" } + } + "NTFS_COMPRESSED" { + return [pscustomobject]@{ Name = "NTFS compressed"; FormatFs = "NTFS"; UdfRevision = $null; UdfDuplicate = $false; Compression = "all" } + } + "NTFS_MIXED" { + return [pscustomobject]@{ Name = "NTFS mixed"; FormatFs = "NTFS"; UdfRevision = $null; UdfDuplicate = $false; Compression = "mixed" } + } + "UDF_102" { + return [pscustomobject]@{ Name = "UDF v1.02"; FormatFs = "UDF"; UdfRevision = "1.02"; UdfDuplicate = $false; Compression = "none" } + } + "UDF_150" { + return [pscustomobject]@{ Name = "UDF v1.50"; FormatFs = "UDF"; UdfRevision = "1.50"; UdfDuplicate = $false; Compression = "none" } + } + "UDF_200" { + return [pscustomobject]@{ Name = "UDF v2.00"; FormatFs = "UDF"; UdfRevision = "2.00"; UdfDuplicate = $false; Compression = "none" } + } + "UDF_201" { + return [pscustomobject]@{ Name = "UDF v2.01"; FormatFs = "UDF"; UdfRevision = "2.01"; UdfDuplicate = $false; Compression = "none" } + } + "UDF_250" { + return [pscustomobject]@{ Name = "UDF v2.50"; FormatFs = "UDF"; UdfRevision = "2.50"; UdfDuplicate = $false; Compression = "none" } + } + "UDF_250_DUP" { + return [pscustomobject]@{ Name = "UDF v2.50 duplicated metadata"; FormatFs = "UDF"; UdfRevision = "2.50"; UdfDuplicate = $true; Compression = "none" } + } + default { + throw "Unsupported filesystem test case: $Name" + } + } +} + +function Format-TestVolume { + param( + [pscustomobject]$FileSystem, + [string]$Drive + ) + + $formatMode = if ($FullFormat) { "" } else { "quick" } + if ($FileSystem.FormatFs -eq "UDF") { + $args = @($Drive, "/FS:UDF", "/V:UDFRAGCI", "/Y") + if (-not $FullFormat) { + $args += "/Q" + } + if ($FileSystem.UdfRevision) { + $args += "/R:$($FileSystem.UdfRevision)" + } + if ($FileSystem.UdfDuplicate) { + $args += "/D" + } + Invoke-Checked -FilePath "format.com" -ArgumentList $args + return + } + + Invoke-DiskPart @( + "select volume $($Drive.TrimEnd(':'))", + "format fs=$($FileSystem.FormatFs) label=UDFRAGCI $formatMode" + ) +} + +function Get-ExtentCount { + param([string]$Path) + + $output = & fsutil.exe file queryextents $Path 2>$null + if ($LASTEXITCODE -ne 0) { + return 0 + } + + return (($output | Select-String -Pattern "VCN:").Count) +} + +function New-SizedFile { + param( + [string]$Path, + [int]$SizeMB + ) + + Invoke-Checked -FilePath "fsutil.exe" -ArgumentList @( + "file", "createnew", $Path, (($SizeMB * 1MB).ToString()) + ) +} + +function New-FragmentedDataset { + param( + [string]$Root, + [string]$CompressionMode + ) + + $fillerRoot = Join-Path $Root "filler" + $fragmentRoot = Join-Path $Root "fragmented" + New-Item -ItemType Directory -Force -Path $fillerRoot, $fragmentRoot | Out-Null + + if ($CompressionMode -eq "all") { + Invoke-Checked -FilePath "compact.exe" -ArgumentList @("/C", $fillerRoot, $fragmentRoot) + } + + Write-Step "Creating allocation pressure files" + for ($i = 0; $i -lt $FillerFileCount; $i++) { + $file = Join-Path $fillerRoot ("filler-{0:D4}.bin" -f $i) + New-SizedFile -Path $file -SizeMB $FillerFileSizeMB + } + + Write-Step "Removing alternating files to create free-space holes" + for ($i = 0; $i -lt $FillerFileCount; $i += 2) { + Remove-Item -Force (Join-Path $fillerRoot ("filler-{0:D4}.bin" -f $i)) + } + + Write-Step "Creating test files expected to span multiple holes" + for ($i = 0; $i -lt $FragmentedFileCount; $i++) { + $file = Join-Path $fragmentRoot ("fragmented-{0:D4}.bin" -f $i) + New-SizedFile -Path $file -SizeMB $FragmentedFileSizeMB + if ($CompressionMode -eq "mixed" -and ($i % 4) -eq 0) { + Invoke-Checked -FilePath "compact.exe" -ArgumentList @("/C", $file) + } + } + + if ($CompressionMode -eq "all") { + Invoke-Checked -FilePath "compact.exe" -ArgumentList @("/C", "/S:$Root") + } + + $fragmentedFiles = Get-ChildItem -Path $fragmentRoot -Filter "*.bin" + $extentCounts = foreach ($file in $fragmentedFiles) { + Get-ExtentCount -Path $file.FullName + } + + $fragmentedCount = ($extentCounts | Where-Object { $_ -gt 1 }).Count + $maxExtents = ($extentCounts | Measure-Object -Maximum).Maximum + if ($null -eq $maxExtents) { + $maxExtents = 0 + } + + Write-Host "Fragmented files detected: $fragmentedCount/$($fragmentedFiles.Count); max extents: $maxExtents" + if ($fragmentedCount -eq 0) { + Write-Warning "No fragmented files were detected. Continuing because allocation behavior can vary on virtual disks." + } +} + +function Invoke-FragmentedVolumeCase { + param( + [pscustomobject]$FileSystem, + [string]$Binary, + [string]$ResolvedDriveLetter + ) + + $drive = "$ResolvedDriveLetter`:" + $safeName = ($FileSystem.Name -replace "[^A-Za-z0-9]+", "-").Trim("-").ToLowerInvariant() + $vhdPath = Join-Path $env:TEMP "udefrag-fragmented-volume-$safeName.vhd" + $testRoot = "$drive\udefrag-ci" + + try { + Write-Step "Creating temporary ${VolumeSizeMB}MB VHD for $($FileSystem.Name) at $vhdPath" + Remove-Item -Path $vhdPath -Force -ErrorAction SilentlyContinue + Invoke-DiskPart @( + "create vdisk file=`"$vhdPath`" maximum=$VolumeSizeMB type=fixed", + "select vdisk file=`"$vhdPath`"", + "attach vdisk", + "create partition primary", + "assign letter=$ResolvedDriveLetter" + ) + + Write-Step "Formatting test volume as $($FileSystem.Name)" + Format-TestVolume -FileSystem $FileSystem -Drive $drive + + Write-Step "Running pre-test CHKDSK for $($FileSystem.Name)" + Invoke-Checked -FilePath "chkdsk.exe" -ArgumentList @($drive) + + New-FragmentedDataset -Root $testRoot -CompressionMode $FileSystem.Compression + + Write-Step "Analyzing fragmented $($FileSystem.Name) test volume" + Invoke-Checked -FilePath $Binary -ArgumentList @("--analyze", $drive) + + Write-Step "Running dry-run defragmentation against $($FileSystem.Name) test volume" + $env:UD_DRY_RUN = "1" + Invoke-Checked -FilePath $Binary -ArgumentList @("--defragment", $drive) + + Write-Step "Running post-test CHKDSK for $($FileSystem.Name)" + Invoke-Checked -FilePath "chkdsk.exe" -ArgumentList @($drive) + } finally { + Remove-Item Env:\UD_DRY_RUN -ErrorAction SilentlyContinue + + Write-Step "Detaching and deleting temporary VHD for $($FileSystem.Name)" + try { + Invoke-DiskPart @( + "select vdisk file=`"$vhdPath`"", + "detach vdisk" + ) + } catch { + Write-Warning $_ + } + Remove-Item -Path $vhdPath -Force -ErrorAction SilentlyContinue + } +} + +if (-not (Test-Administrator)) { + throw "The fragmented volume test requires an elevated Windows runner." +} + +$binary = Resolve-Path $BinaryPath +$resolvedDriveLetter = Resolve-TestDriveLetter -PreferredDriveLetter $DriveLetter +$fileSystemCases = foreach ($fs in $FileSystems) { + Get-TestFileSystem -Name $fs +} + +foreach ($fs in $fileSystemCases) { + Write-Step "Starting filesystem case: $($fs.Name)" + Invoke-FragmentedVolumeCase -FileSystem $fs -Binary $binary.Path -ResolvedDriveLetter $resolvedDriveLetter +} diff --git a/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.sh b/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.sh new file mode 100755 index 0000000..0cf35c8 --- /dev/null +++ b/TestSuite/Create Fragmented Volumes/ci_fragmented_volume_test.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +binary="${1:-./udefrag}" +volume_size_mb="${VOLUME_SIZE_MB:-1024}" +filler_file_count="${FILLER_FILE_COUNT:-300}" +filler_file_size_mb="${FILLER_FILE_SIZE_MB:-3}" +fragmented_file_count="${FRAGMENTED_FILE_COUNT:-40}" +fragmented_file_size_mb="${FRAGMENTED_FILE_SIZE_MB:-5}" +supported_filesystems="${SUPPORTED_FILESYSTEMS:-ntfs}" + +if [[ "$(id -u)" -ne 0 ]]; then + echo "This test creates and mounts a temporary NTFS image; run it as root." >&2 + exit 1 +fi + +if [[ ! -x "$binary" ]]; then + echo "Binary not found or not executable: $binary" >&2 + exit 1 +fi + +for tool in mkntfs ntfs-3g ntfsfix umount dd truncate; do + if ! command -v "$tool" >/dev/null 2>&1; then + echo "Required tool is missing: $tool" >&2 + exit 1 + fi +done + +workdir="$(mktemp -d "${TMPDIR:-/tmp}/udefrag-fragmented-volume.XXXXXX")" +mountpoint="$workdir/mnt" +mounted=0 + +cleanup() { + if [[ "$mounted" -eq 1 ]]; then + sync || true + umount "$mountpoint" || true + fi + rm -rf "$workdir" +} +trap cleanup EXIT + +step() { + printf '==> %s\n' "$*" +} + +run() { + printf '+' + printf ' %q' "$@" + printf '\n' + "$@" +} + +create_file() { + local path="$1" + local size_mb="$2" + + dd if=/dev/zero of="$path" bs=1M count="$size_mb" status=none conv=fsync +} + +run_ntfs_case() { + local image="$workdir/udefrag-fragmented-volume-ntfs.img" + local filler_root + local fragment_root + + step "Creating temporary ${volume_size_mb}MB NTFS image at $image" + mkdir -p "$mountpoint" + run truncate -s "${volume_size_mb}M" "$image" + run mkntfs -F -Q -L UDFRAGCI "$image" + + step "Checking fresh NTFS image" + run ntfsfix -n "$image" + + step "Mounting NTFS image" + run ntfs-3g "$image" "$mountpoint" + mounted=1 + + filler_root="$mountpoint/udefrag-ci/filler" + fragment_root="$mountpoint/udefrag-ci/fragmented" + mkdir -p "$filler_root" "$fragment_root" + + step "Creating allocation pressure files" + for ((i = 0; i < filler_file_count; i++)); do + create_file "$filler_root/filler-$(printf '%04d' "$i").bin" "$filler_file_size_mb" + done + + step "Removing alternating files to create free-space holes" + for ((i = 0; i < filler_file_count; i += 2)); do + rm -f "$filler_root/filler-$(printf '%04d' "$i").bin" + done + + step "Creating test files expected to span multiple holes" + for ((i = 0; i < fragmented_file_count; i++)); do + create_file "$fragment_root/fragmented-$(printf '%04d' "$i").bin" "$fragmented_file_size_mb" + done + + sync + step "Unmounting NTFS image before defragmenter run" + run umount "$mountpoint" + mounted=0 + + step "Checking populated NTFS image" + run ntfsfix -n "$image" + + step "Analyzing fragmented NTFS image" + run "$binary" --analyze "$image" + + step "Running dry-run defragmentation against fragmented NTFS image" + run env UD_DRY_RUN=1 "$binary" --defragment "$image" + + step "Checking NTFS image after dry-run" + run ntfsfix -n "$image" +} + +for fs in $supported_filesystems; do + case "${fs,,}" in + ntfs) + step "Starting filesystem case: NTFS" + run_ntfs_case + ;; + *) + echo "Unsupported Linux filesystem test case: $fs" >&2 + echo "The Linux build currently supports NTFS device images only." >&2 + exit 1 + ;; + esac +done diff --git a/ci/build-windows-native.sh b/ci/build-windows-native.sh new file mode 100755 index 0000000..5b76413 --- /dev/null +++ b/ci/build-windows-native.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$repo_root" + +export BUILD_ENV=mingw_x64 +export COMPILER_BIN=/ucrt64/bin/ +export UDEFRAG_LIB_PATH=../../lib/amd64 + +# mkmod.lua invokes gmake for x64 MinGW builds. MSYS2 provides make. +mkdir -p ci/bin src/bin/amd64 src/lib/amd64 +cat > ci/bin/gmake <<'EOF' +#!/usr/bin/env sh +exec make "$@" +EOF +chmod +x ci/bin/gmake +export PATH="$repo_root/ci/bin:$PATH" + +# The legacy Windows build staged getopt sources before generating the +# console makefile. +cp src/share/getopt.c src/share/getopt1.c src/share/getopt.h src/console/ + +build_module() { + local module_dir="$1" + local build_file="$2" + + ( + cd "$module_dir" + lua ../../tools/mkmod.lua "$build_file" + ) +} + +( + cd src/lua5.1 + lua ../tools/mkmod.lua lua5.1a_dll.build +) + +build_module src/dll/wgx wgx.build +build_module src/dll/zenwinx zenwinx.build +build_module src/dll/udefrag udefrag.build + +( + cd src/console + lua ../tools/mkmod.lua defrag.build +) + +test -f src/bin/amd64/udefrag.exe +file src/bin/amd64/udefrag.exe diff --git a/src/console/defrag.c b/src/console/defrag.c index 7cbee6b..103737d 100644 --- a/src/console/defrag.c +++ b/src/console/defrag.c @@ -469,7 +469,7 @@ void update_progress(udefrag_progress_info *pi, void *p) char *op_name = ""; char *results; #if defined(CURSES) | defined(LINUX) - char buf[120]; + char buf[512]; #ifdef LINUX char *volume; #else @@ -522,17 +522,17 @@ void update_progress(udefrag_progress_info *pi, void *p) #endif if(pi->current_operation == VOLUME_OPTIMIZATION && !stop_flag && pi->completion_status == 0){ if(pi->pass_number > 1) - sprintf(buf,"%s: %s%6.2lf%% complete, pass %lu, moves total = %" LL64 "u", + snprintf(buf,sizeof(buf),"%s: %s%6.2lf%% complete, pass %lu, moves total = %" LL64 "u", volume,op_name,pi->percentage,pi->pass_number,(ULONGLONG)pi->total_moves); else - sprintf(buf,"%s: %s%6.2lf%% complete, moves total = %" LL64 "u", + snprintf(buf,sizeof(buf),"%s: %s%6.2lf%% complete, moves total = %" LL64 "u", volume,op_name,pi->percentage,(ULONGLONG)pi->total_moves); } else { if(pi->pass_number > 1) - sprintf(buf,"%s: %s%6.2lf%% complete, pass %lu, fragmented/total = %lu/%lu", + snprintf(buf,sizeof(buf),"%s: %s%6.2lf%% complete, pass %lu, fragmented/total = %lu/%lu", volume,op_name,pi->percentage,pi->pass_number,pi->fragmented,pi->files); else - sprintf(buf,"%s: %s%6.2lf%% complete, fragmented/total = %lu/%lu", + snprintf(buf,sizeof(buf),"%s: %s%6.2lf%% complete, fragmented/total = %lu/%lu", volume,op_name,pi->percentage,pi->fragmented,pi->files); } if (m_flag) @@ -543,10 +543,10 @@ void update_progress(udefrag_progress_info *pi, void *p) if (pi->completion_status != 0 && !stop_flag) { /* set progress indicator to 100% state */ if(pi->pass_number > 1) - sprintf(buf,"%s: %s100.00%% complete, %lu passes needed, fragmented/total = %lu/%lu", + snprintf(buf,sizeof(buf),"%s: %s100.00%% complete, %lu passes needed, fragmented/total = %lu/%lu", volume,op_name,pi->pass_number,pi->fragmented,pi->files); else - sprintf(buf,"%s: %s100.00%% complete, fragmented/total = %lu/%lu", + snprintf(buf,sizeof(buf),"%s: %s100.00%% complete, fragmented/total = %lu/%lu", volume,op_name,pi->fragmented,pi->files); if (m_flag) set_message(ROW_PROGRESS,0, diff --git a/src/tools/mkmod.lua b/src/tools/mkmod.lua index 6d6bb29..b1fc2f2 100755 --- a/src/tools/mkmod.lua +++ b/src/tools/mkmod.lua @@ -130,11 +130,11 @@ function produce_ddk_makefile() f:write("USER_C_FLAGS=/DUSE_WINDDK\n\n") if static_lib == 1 then - f:write("USER_C_FLAGS=\$(USER_C_FLAGS) /DSTATIC_LIB\n\n") + f:write("USER_C_FLAGS=$(USER_C_FLAGS) /DSTATIC_LIB\n\n") end if target_type == "console" or target_type == "gui" then - f:write("CFLAGS=\$(CFLAGS) /MT\n\n") + f:write("CFLAGS=$(CFLAGS) /MT\n\n") end f:write("SOURCES=") @@ -150,7 +150,7 @@ function produce_ddk_makefile() -- workaround for WDK 7 if os.getenv("UD_DDK_VER") == "7600" then - f:write("MINWIN_SDK_LIB_PATH=\$(SDK_LIB_PATH)\n") + f:write("MINWIN_SDK_LIB_PATH=$(SDK_LIB_PATH)\n") end end if target_type == "dll" then @@ -171,7 +171,7 @@ function produce_ddk_makefile() f:write("LINKLIBS=") for i, v in ipairs(libs) do if v ~= "msvcrt" then - f:write("\$(DDK_LIB_PATH)\\", v, ".lib ") + f:write("$(DDK_LIB_PATH)\\", v, ".lib ") end end for i, v in ipairs(adlibs) do f:write(v, ".lib ") end @@ -196,7 +196,7 @@ function produce_msvc_makefile() --[[ OUTDIR and INTDIR parameters are replaced with current directory - f:write("!IF \"\$(OS)\" == \"Windows_NT\"\n") + f:write("!IF \"$(OS)\" == \"Windows_NT\"\n") f:write("NULL=\n") f:write("!ELSE\n") f:write("NULL=nul\n") @@ -227,7 +227,7 @@ function produce_msvc_makefile() cl_flags = cl_flags .. "/D \"_CONSOLE\" /D \"_USRDLL\" /D \"" .. upname .. "\" " s = "console" elseif target_type == "driver" then - cl_flags = cl_flags .. "/I \"\$(ROSINCDIR)\" /I \"\$(ROSINCDIR)\\ddk\" " + cl_flags = cl_flags .. "/I \"$(ROSINCDIR)\" /I \"$(ROSINCDIR)\\ddk\" " s = "native" elseif target_type == "native" then s = "native" @@ -294,8 +294,8 @@ function produce_msvc_makefile() f:write("CPP=cl.exe\nRSC=rc.exe\nLINK32=link.exe\n\n") f:write(".c.obj::\n") - f:write(" \$(CPP) \@<<\n") - f:write(" \$(CPP_PROJ) \$<\n") + f:write(" $(CPP) @<<\n") + f:write(" $(CPP_PROJ) $<\n") f:write("<<\n\n") f:write("LINK32_OBJS=") @@ -309,21 +309,21 @@ function produce_msvc_makefile() if target_type == "dll" then f:write("DEF_FILE=", deffile, "\n\n") - f:write("\"", name, ".", target_ext, "\" : \$(DEF_FILE) \$(LINK32_OBJS)\n") - f:write(" \$(LINK32) \@<<\n") - f:write(" \$(LINK32_FLAGS) \$(LINK32_OBJS)\n") + f:write("\"", name, ".", target_ext, "\" : $(DEF_FILE) $(LINK32_OBJS)\n") + f:write(" $(LINK32) @<<\n") + f:write(" $(LINK32_FLAGS) $(LINK32_OBJS)\n") f:write("<<\n\n") else - f:write("\"", name, ".", target_ext, "\" : \$(LINK32_OBJS)\n") - f:write(" \$(LINK32) \@<<\n") - f:write(" \$(LINK32_FLAGS) \$(LINK32_OBJS)\n") + f:write("\"", name, ".", target_ext, "\" : $(LINK32_OBJS)\n") + f:write(" $(LINK32) @<<\n") + f:write(" $(LINK32_FLAGS) $(LINK32_OBJS)\n") f:write("<<\n\n") end for i, v in ipairs(rc) do f:write("SOURCE=", v, "\n\n") - f:write(string.gsub(v,"%.rc","%.res"), " : \$(SOURCE)\n") - f:write(" \$(RSC) \$(RSC_PROJ) \$(SOURCE)\n\n") + f:write(string.gsub(v,"%.rc","%.res"), " : $(SOURCE)\n") + f:write(" $(RSC) $(RSC_PROJ) $(SOURCE)\n\n") end f:close() @@ -375,7 +375,7 @@ function produce_mingw_makefile() local f = assert(io.open(".\\Makefile.mingw","w")) f:write("PROJECT = ", name, "\nCC = gcc.exe\n\n") - f:write("WINDRES = \"\$(COMPILER_BIN)windres.exe\"\n\n") + f:write("WINDRES = \"$(COMPILER_BIN)windres.exe\"\n\n") f:write("TARGET = ", target_name, "\n") @@ -388,9 +388,9 @@ function produce_mingw_makefile() f:write("RCFLAGS = ") f:write("\n") - f:write("C_INCLUDE_DIRS = \n") + f:write("C_INCLUDE_DIRS = -I../include -I../../include\n") f:write("C_PREPROC = \n") - f:write("RC_INCLUDE_DIRS = \n") + f:write("RC_INCLUDE_DIRS = -I../include -I../../include\n") f:write("RC_PREPROC = \n") if target_type == "console" then @@ -399,16 +399,16 @@ function produce_mingw_makefile() f:write("LDFLAGS = -pipe -mwindows -Wl,--strip-all\n") elseif target_type == "native" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write("-Wl,--entry,_NtProcessStartup\@4,--subsystem,native,--strip-all\n") + f:write("-Wl,--entry,_NtProcessStartup@4,--subsystem,native,--strip-all\n") elseif target_type == "driver" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry\@8,") + f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry@8,") f:write("--subsystem,native,--image-base,0x10000,-shared,--strip-all\n") elseif target_type == "dll" then f:write("LDFLAGS = -pipe -shared -Wl,") f:write("--out-implib,lib", name, ".dll.a -nostartfiles ") f:write("-nodefaultlibs ", mingw_deffile, " -Wl,--kill-at,") - f:write("--entry,_DllMain\@12,--strip-all\n") + f:write("--entry,_DllMain@12,--strip-all\n") else error("Unknown target type: " .. target_type .. "!") end @@ -459,32 +459,32 @@ function produce_mingw_makefile() --end f:write(".PHONY: print_header\n\n") - f:write("\$(TARGET): print_header \$(RSRC_OBJS) \$(SRC_OBJS)\n") - f:write("\t\$(build_target)\n") + f:write("$(TARGET): print_header $(RSRC_OBJS) $(SRC_OBJS)\n") + f:write("\t$(build_target)\n") if target_type == "dll" then - f:write("\t\$(correct_lib)\n") + f:write("\t$(correct_lib)\n") end f:write("\nprint_header:\n") - f:write("\t\@echo ----------Configuration: ", name, " - Release----------\n\n") + f:write("\t@echo ----------Configuration: ", name, " - Release----------\n\n") if target_type == "dll" then f:write("define correct_lib\n") - f:write("\t\@echo ------ correct the lib\$(PROJECT).dll.a library ------\n") - f:write("\t\@dlltool -k --output-lib lib\$(PROJECT).dll.a --def ") + f:write("\t@echo ------ correct the lib$(PROJECT).dll.a library ------\n") + f:write("\t@dlltool -k --output-lib lib$(PROJECT).dll.a --def ") f:write(mingw_deffile, "\n") f:write("endef\n\n") end for i, v in ipairs(src) do f:write(string.gsub(v,"%.c","%.o"), ": ") - f:write(v, "\n\t\$(compile_source)\n\n") + f:write(v, "\n\t$(compile_source)\n\n") end for i, v in ipairs(rc) do f:write(string.gsub(v,"%.rc","%.res"), ": ") - f:write(v, "\n\t\$(compile_resource)\n\n") + f:write(v, "\n\t$(compile_resource)\n\n") end f:close() @@ -499,16 +499,16 @@ function produce_mingw_x64_makefile() local f = assert(io.open(".\\Makefile_x64.mingw","w")) f:write("PROJECT = ", name, "\nCC = x86_64-w64-mingw32-gcc.exe\n\n") - f:write("WINDRES = \"\$(COMPILER_BIN)x86_64-w64-mingw32-windres.exe\"\n\n") + f:write("WINDRES = \"$(COMPILER_BIN)x86_64-w64-mingw32-windres.exe\"\n\n") f:write("TARGET = ", target_name, "\n") f:write("CFLAGS = -pipe -Wall -g0 -O2 -m64\n") f:write("RCFLAGS = \n") - f:write("C_INCLUDE_DIRS = \n") + f:write("C_INCLUDE_DIRS = -I../include -I../../include\n") f:write("C_PREPROC = \n") - f:write("RC_INCLUDE_DIRS = \n") + f:write("RC_INCLUDE_DIRS = -I../include -I../../include\n") f:write("RC_PREPROC = \n") if target_type == "console" then @@ -517,10 +517,10 @@ function produce_mingw_x64_makefile() f:write("LDFLAGS = -pipe -mwindows -Wl,--strip-all\n") elseif target_type == "native" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write("-Wl,--entry,_NtProcessStartup\@4,--subsystem,native,--strip-all\n") + f:write("-Wl,--entry,_NtProcessStartup@4,--subsystem,native,--strip-all\n") elseif target_type == "driver" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry\@8,") + f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry@8,") f:write("--subsystem,native,--image-base,0x10000,-shared,--strip-all\n") elseif target_type == "dll" then f:write("LDFLAGS = -pipe -shared -Wl,") @@ -530,7 +530,7 @@ function produce_mingw_x64_makefile() else f:write("-nodefaultlibs ", mingw_deffile, " -Wl,--kill-at,") end - f:write("--entry,_DllMain\@12,--strip-all\n") + f:write("--entry,_DllMain@12,--strip-all\n") else error("Unknown target type: " .. target_type .. "!") end @@ -577,32 +577,32 @@ function produce_mingw_x64_makefile() f:write(main_mingw_rules) f:write(".PHONY: print_header\n\n") - f:write("\$(TARGET): print_header \$(RSRC_OBJS) \$(SRC_OBJS)\n") - f:write("\t\$(build_target)\n") + f:write("$(TARGET): print_header $(RSRC_OBJS) $(SRC_OBJS)\n") + f:write("\t$(build_target)\n") if target_type == "dll" then - f:write("\t\$(correct_lib)\n") + f:write("\t$(correct_lib)\n") end f:write("\nprint_header:\n") - f:write("\t\@echo ----------Configuration: ", name, " - Release----------\n\n") + f:write("\t@echo ----------Configuration: ", name, " - Release----------\n\n") if target_type == "dll" then f:write("define correct_lib\n") - f:write("\t\@echo ------ correct the lib\$(PROJECT).dll.a library ------\n") - f:write("\t\@x86_64-w64-mingw32-dlltool -k --output-lib lib\$(PROJECT).dll.a --def ") + f:write("\t@echo ------ correct the lib$(PROJECT).dll.a library ------\n") + f:write("\t@x86_64-w64-mingw32-dlltool -k --output-lib lib$(PROJECT).dll.a --def ") f:write(deffile, "\n") f:write("endef\n\n") end for i, v in ipairs(src) do f:write(string.gsub(v,"%.c","%.o"), ": ") - f:write(v, "\n\t\$(compile_source)\n\n") + f:write(v, "\n\t$(compile_source)\n\n") end for i, v in ipairs(rc) do f:write(string.gsub(v,"%.rc","%.res"), ": ") - f:write(v, "\n\t\$(compile_resource)\n\n") + f:write(v, "\n\t$(compile_resource)\n\n") end f:close() @@ -741,7 +741,7 @@ elseif os.getenv("BUILD_ENV") == "mingw_x64" then end end else - error("\%BUILD_ENV\% has wrong value: " .. os.getenv("BUILD_ENV") .. "!") + error("%BUILD_ENV% has wrong value: " .. os.getenv("BUILD_ENV") .. "!") end print(input_filename .. " " .. os.getenv("BUILD_ENV") .. " build was successful.\n") From 2c1af7d4eb5e71a48a21c01baf7ee8e528e5283e Mon Sep 17 00:00:00 2001 From: Jan Giesenberg Date: Sun, 17 May 2026 14:07:43 +0200 Subject: [PATCH 5/5] ci: remove windows build --- .github/workflows/build.yml | 36 --------------- ci/build-windows-native.sh | 49 -------------------- src/tools/mkmod.lua | 90 ++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 130 deletions(-) delete mode 100755 ci/build-windows-native.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b33fd1d..1baa848 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -91,39 +91,3 @@ jobs: path: udefrag if-no-files-found: error - windows: - name: Windows native build - runs-on: windows-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: UCRT64 - update: true - install: >- - base-devel - make - mingw-w64-ucrt-x86_64-binutils - mingw-w64-ucrt-x86_64-gcc - mingw-w64-ucrt-x86_64-lua - - - name: Build native Windows binary - shell: msys2 {0} - run: ./ci/build-windows-native.sh - - - name: Test fragmented NTFS volume - shell: pwsh - run: | - $env:PATH = "C:\msys64\ucrt64\bin;$env:PATH" - & ".\TestSuite\Create Fragmented Volumes\ci_fragmented_volume_test.ps1" - - - name: Upload binary - uses: actions/upload-artifact@v4 - with: - name: udefrag-windows-x64 - path: src/bin/amd64/* - if-no-files-found: error diff --git a/ci/build-windows-native.sh b/ci/build-windows-native.sh deleted file mode 100755 index 5b76413..0000000 --- a/ci/build-windows-native.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -cd "$repo_root" - -export BUILD_ENV=mingw_x64 -export COMPILER_BIN=/ucrt64/bin/ -export UDEFRAG_LIB_PATH=../../lib/amd64 - -# mkmod.lua invokes gmake for x64 MinGW builds. MSYS2 provides make. -mkdir -p ci/bin src/bin/amd64 src/lib/amd64 -cat > ci/bin/gmake <<'EOF' -#!/usr/bin/env sh -exec make "$@" -EOF -chmod +x ci/bin/gmake -export PATH="$repo_root/ci/bin:$PATH" - -# The legacy Windows build staged getopt sources before generating the -# console makefile. -cp src/share/getopt.c src/share/getopt1.c src/share/getopt.h src/console/ - -build_module() { - local module_dir="$1" - local build_file="$2" - - ( - cd "$module_dir" - lua ../../tools/mkmod.lua "$build_file" - ) -} - -( - cd src/lua5.1 - lua ../tools/mkmod.lua lua5.1a_dll.build -) - -build_module src/dll/wgx wgx.build -build_module src/dll/zenwinx zenwinx.build -build_module src/dll/udefrag udefrag.build - -( - cd src/console - lua ../tools/mkmod.lua defrag.build -) - -test -f src/bin/amd64/udefrag.exe -file src/bin/amd64/udefrag.exe diff --git a/src/tools/mkmod.lua b/src/tools/mkmod.lua index b1fc2f2..6d6bb29 100755 --- a/src/tools/mkmod.lua +++ b/src/tools/mkmod.lua @@ -130,11 +130,11 @@ function produce_ddk_makefile() f:write("USER_C_FLAGS=/DUSE_WINDDK\n\n") if static_lib == 1 then - f:write("USER_C_FLAGS=$(USER_C_FLAGS) /DSTATIC_LIB\n\n") + f:write("USER_C_FLAGS=\$(USER_C_FLAGS) /DSTATIC_LIB\n\n") end if target_type == "console" or target_type == "gui" then - f:write("CFLAGS=$(CFLAGS) /MT\n\n") + f:write("CFLAGS=\$(CFLAGS) /MT\n\n") end f:write("SOURCES=") @@ -150,7 +150,7 @@ function produce_ddk_makefile() -- workaround for WDK 7 if os.getenv("UD_DDK_VER") == "7600" then - f:write("MINWIN_SDK_LIB_PATH=$(SDK_LIB_PATH)\n") + f:write("MINWIN_SDK_LIB_PATH=\$(SDK_LIB_PATH)\n") end end if target_type == "dll" then @@ -171,7 +171,7 @@ function produce_ddk_makefile() f:write("LINKLIBS=") for i, v in ipairs(libs) do if v ~= "msvcrt" then - f:write("$(DDK_LIB_PATH)\\", v, ".lib ") + f:write("\$(DDK_LIB_PATH)\\", v, ".lib ") end end for i, v in ipairs(adlibs) do f:write(v, ".lib ") end @@ -196,7 +196,7 @@ function produce_msvc_makefile() --[[ OUTDIR and INTDIR parameters are replaced with current directory - f:write("!IF \"$(OS)\" == \"Windows_NT\"\n") + f:write("!IF \"\$(OS)\" == \"Windows_NT\"\n") f:write("NULL=\n") f:write("!ELSE\n") f:write("NULL=nul\n") @@ -227,7 +227,7 @@ function produce_msvc_makefile() cl_flags = cl_flags .. "/D \"_CONSOLE\" /D \"_USRDLL\" /D \"" .. upname .. "\" " s = "console" elseif target_type == "driver" then - cl_flags = cl_flags .. "/I \"$(ROSINCDIR)\" /I \"$(ROSINCDIR)\\ddk\" " + cl_flags = cl_flags .. "/I \"\$(ROSINCDIR)\" /I \"\$(ROSINCDIR)\\ddk\" " s = "native" elseif target_type == "native" then s = "native" @@ -294,8 +294,8 @@ function produce_msvc_makefile() f:write("CPP=cl.exe\nRSC=rc.exe\nLINK32=link.exe\n\n") f:write(".c.obj::\n") - f:write(" $(CPP) @<<\n") - f:write(" $(CPP_PROJ) $<\n") + f:write(" \$(CPP) \@<<\n") + f:write(" \$(CPP_PROJ) \$<\n") f:write("<<\n\n") f:write("LINK32_OBJS=") @@ -309,21 +309,21 @@ function produce_msvc_makefile() if target_type == "dll" then f:write("DEF_FILE=", deffile, "\n\n") - f:write("\"", name, ".", target_ext, "\" : $(DEF_FILE) $(LINK32_OBJS)\n") - f:write(" $(LINK32) @<<\n") - f:write(" $(LINK32_FLAGS) $(LINK32_OBJS)\n") + f:write("\"", name, ".", target_ext, "\" : \$(DEF_FILE) \$(LINK32_OBJS)\n") + f:write(" \$(LINK32) \@<<\n") + f:write(" \$(LINK32_FLAGS) \$(LINK32_OBJS)\n") f:write("<<\n\n") else - f:write("\"", name, ".", target_ext, "\" : $(LINK32_OBJS)\n") - f:write(" $(LINK32) @<<\n") - f:write(" $(LINK32_FLAGS) $(LINK32_OBJS)\n") + f:write("\"", name, ".", target_ext, "\" : \$(LINK32_OBJS)\n") + f:write(" \$(LINK32) \@<<\n") + f:write(" \$(LINK32_FLAGS) \$(LINK32_OBJS)\n") f:write("<<\n\n") end for i, v in ipairs(rc) do f:write("SOURCE=", v, "\n\n") - f:write(string.gsub(v,"%.rc","%.res"), " : $(SOURCE)\n") - f:write(" $(RSC) $(RSC_PROJ) $(SOURCE)\n\n") + f:write(string.gsub(v,"%.rc","%.res"), " : \$(SOURCE)\n") + f:write(" \$(RSC) \$(RSC_PROJ) \$(SOURCE)\n\n") end f:close() @@ -375,7 +375,7 @@ function produce_mingw_makefile() local f = assert(io.open(".\\Makefile.mingw","w")) f:write("PROJECT = ", name, "\nCC = gcc.exe\n\n") - f:write("WINDRES = \"$(COMPILER_BIN)windres.exe\"\n\n") + f:write("WINDRES = \"\$(COMPILER_BIN)windres.exe\"\n\n") f:write("TARGET = ", target_name, "\n") @@ -388,9 +388,9 @@ function produce_mingw_makefile() f:write("RCFLAGS = ") f:write("\n") - f:write("C_INCLUDE_DIRS = -I../include -I../../include\n") + f:write("C_INCLUDE_DIRS = \n") f:write("C_PREPROC = \n") - f:write("RC_INCLUDE_DIRS = -I../include -I../../include\n") + f:write("RC_INCLUDE_DIRS = \n") f:write("RC_PREPROC = \n") if target_type == "console" then @@ -399,16 +399,16 @@ function produce_mingw_makefile() f:write("LDFLAGS = -pipe -mwindows -Wl,--strip-all\n") elseif target_type == "native" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write("-Wl,--entry,_NtProcessStartup@4,--subsystem,native,--strip-all\n") + f:write("-Wl,--entry,_NtProcessStartup\@4,--subsystem,native,--strip-all\n") elseif target_type == "driver" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry@8,") + f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry\@8,") f:write("--subsystem,native,--image-base,0x10000,-shared,--strip-all\n") elseif target_type == "dll" then f:write("LDFLAGS = -pipe -shared -Wl,") f:write("--out-implib,lib", name, ".dll.a -nostartfiles ") f:write("-nodefaultlibs ", mingw_deffile, " -Wl,--kill-at,") - f:write("--entry,_DllMain@12,--strip-all\n") + f:write("--entry,_DllMain\@12,--strip-all\n") else error("Unknown target type: " .. target_type .. "!") end @@ -459,32 +459,32 @@ function produce_mingw_makefile() --end f:write(".PHONY: print_header\n\n") - f:write("$(TARGET): print_header $(RSRC_OBJS) $(SRC_OBJS)\n") - f:write("\t$(build_target)\n") + f:write("\$(TARGET): print_header \$(RSRC_OBJS) \$(SRC_OBJS)\n") + f:write("\t\$(build_target)\n") if target_type == "dll" then - f:write("\t$(correct_lib)\n") + f:write("\t\$(correct_lib)\n") end f:write("\nprint_header:\n") - f:write("\t@echo ----------Configuration: ", name, " - Release----------\n\n") + f:write("\t\@echo ----------Configuration: ", name, " - Release----------\n\n") if target_type == "dll" then f:write("define correct_lib\n") - f:write("\t@echo ------ correct the lib$(PROJECT).dll.a library ------\n") - f:write("\t@dlltool -k --output-lib lib$(PROJECT).dll.a --def ") + f:write("\t\@echo ------ correct the lib\$(PROJECT).dll.a library ------\n") + f:write("\t\@dlltool -k --output-lib lib\$(PROJECT).dll.a --def ") f:write(mingw_deffile, "\n") f:write("endef\n\n") end for i, v in ipairs(src) do f:write(string.gsub(v,"%.c","%.o"), ": ") - f:write(v, "\n\t$(compile_source)\n\n") + f:write(v, "\n\t\$(compile_source)\n\n") end for i, v in ipairs(rc) do f:write(string.gsub(v,"%.rc","%.res"), ": ") - f:write(v, "\n\t$(compile_resource)\n\n") + f:write(v, "\n\t\$(compile_resource)\n\n") end f:close() @@ -499,16 +499,16 @@ function produce_mingw_x64_makefile() local f = assert(io.open(".\\Makefile_x64.mingw","w")) f:write("PROJECT = ", name, "\nCC = x86_64-w64-mingw32-gcc.exe\n\n") - f:write("WINDRES = \"$(COMPILER_BIN)x86_64-w64-mingw32-windres.exe\"\n\n") + f:write("WINDRES = \"\$(COMPILER_BIN)x86_64-w64-mingw32-windres.exe\"\n\n") f:write("TARGET = ", target_name, "\n") f:write("CFLAGS = -pipe -Wall -g0 -O2 -m64\n") f:write("RCFLAGS = \n") - f:write("C_INCLUDE_DIRS = -I../include -I../../include\n") + f:write("C_INCLUDE_DIRS = \n") f:write("C_PREPROC = \n") - f:write("RC_INCLUDE_DIRS = -I../include -I../../include\n") + f:write("RC_INCLUDE_DIRS = \n") f:write("RC_PREPROC = \n") if target_type == "console" then @@ -517,10 +517,10 @@ function produce_mingw_x64_makefile() f:write("LDFLAGS = -pipe -mwindows -Wl,--strip-all\n") elseif target_type == "native" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write("-Wl,--entry,_NtProcessStartup@4,--subsystem,native,--strip-all\n") + f:write("-Wl,--entry,_NtProcessStartup\@4,--subsystem,native,--strip-all\n") elseif target_type == "driver" then f:write("LDFLAGS = -pipe -nostartfiles -nodefaultlibs ") - f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry@8,") + f:write(mingw_deffile .. " -Wl,--entry,_DriverEntry\@8,") f:write("--subsystem,native,--image-base,0x10000,-shared,--strip-all\n") elseif target_type == "dll" then f:write("LDFLAGS = -pipe -shared -Wl,") @@ -530,7 +530,7 @@ function produce_mingw_x64_makefile() else f:write("-nodefaultlibs ", mingw_deffile, " -Wl,--kill-at,") end - f:write("--entry,_DllMain@12,--strip-all\n") + f:write("--entry,_DllMain\@12,--strip-all\n") else error("Unknown target type: " .. target_type .. "!") end @@ -577,32 +577,32 @@ function produce_mingw_x64_makefile() f:write(main_mingw_rules) f:write(".PHONY: print_header\n\n") - f:write("$(TARGET): print_header $(RSRC_OBJS) $(SRC_OBJS)\n") - f:write("\t$(build_target)\n") + f:write("\$(TARGET): print_header \$(RSRC_OBJS) \$(SRC_OBJS)\n") + f:write("\t\$(build_target)\n") if target_type == "dll" then - f:write("\t$(correct_lib)\n") + f:write("\t\$(correct_lib)\n") end f:write("\nprint_header:\n") - f:write("\t@echo ----------Configuration: ", name, " - Release----------\n\n") + f:write("\t\@echo ----------Configuration: ", name, " - Release----------\n\n") if target_type == "dll" then f:write("define correct_lib\n") - f:write("\t@echo ------ correct the lib$(PROJECT).dll.a library ------\n") - f:write("\t@x86_64-w64-mingw32-dlltool -k --output-lib lib$(PROJECT).dll.a --def ") + f:write("\t\@echo ------ correct the lib\$(PROJECT).dll.a library ------\n") + f:write("\t\@x86_64-w64-mingw32-dlltool -k --output-lib lib\$(PROJECT).dll.a --def ") f:write(deffile, "\n") f:write("endef\n\n") end for i, v in ipairs(src) do f:write(string.gsub(v,"%.c","%.o"), ": ") - f:write(v, "\n\t$(compile_source)\n\n") + f:write(v, "\n\t\$(compile_source)\n\n") end for i, v in ipairs(rc) do f:write(string.gsub(v,"%.rc","%.res"), ": ") - f:write(v, "\n\t$(compile_resource)\n\n") + f:write(v, "\n\t\$(compile_resource)\n\n") end f:close() @@ -741,7 +741,7 @@ elseif os.getenv("BUILD_ENV") == "mingw_x64" then end end else - error("%BUILD_ENV% has wrong value: " .. os.getenv("BUILD_ENV") .. "!") + error("\%BUILD_ENV\% has wrong value: " .. os.getenv("BUILD_ENV") .. "!") end print(input_filename .. " " .. os.getenv("BUILD_ENV") .. " build was successful.\n")