diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9027c9..3dc361f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,8 +32,8 @@ jobs: - name: Build shell: pwsh - run: python build_native.py + run: python scripts/build.py - name: Smoke tests shell: pwsh - run: python core\native\tests\native_smoke.py + run: python proto/tests/native_smoke.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bbb6a10..9efc6f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,24 +37,24 @@ jobs: - name: Build and test shell: pwsh run: | - python build_native.py + python scripts/build.py if ($LASTEXITCODE -ne 0) { throw 'Native build failed' } - python core\native\tests\native_smoke.py + python proto/tests/native_smoke.py if ($LASTEXITCODE -ne 0) { throw 'Smoke tests failed' } - name: Package flat distribution shell: pwsh run: | $files = @( - 'dist/binary/QuickFileCopy.exe', - 'dist/binary/QuickFileCopy_cli.exe', - 'dist/binary/WebView2Loader.dll', - 'dist/documents/readme.txt', - 'dist/documents/readme_jp.txt', - 'dist/documents/history.txt', - 'dist/documents/history_jp.txt', - 'dist/documents/LICENSE.txt', - 'dist/documents/LICENSE_jp.txt' + 'dist/QuickFileCopy.exe', + 'dist/QuickFileCopy_cli.exe', + 'dist/WebView2Loader.dll', + 'docs/distribution/readme.txt', + 'docs/distribution/readme_jp.txt', + 'docs/distribution/history.txt', + 'docs/distribution/history_jp.txt', + 'docs/distribution/LICENSE.txt', + 'docs/distribution/LICENSE_jp.txt' ) foreach ($file in $files) { if (!(Test-Path -LiteralPath $file)) { throw "Missing distribution file: $file" } @@ -64,12 +64,34 @@ jobs: foreach ($file in $files) { Copy-Item -LiteralPath $file -Destination $staging -Force } Compress-Archive -Path (Join-Path $staging '*') -DestinationPath 'QuickFileCopy-binary.zip' -Force + - name: Generate SHA-256 checksums + shell: pwsh + run: | + $checksumFile = Join-Path $env:GITHUB_WORKSPACE 'SHA256SUMS.txt' + $lines = @() + $zipPath = Join-Path $env:GITHUB_WORKSPACE 'QuickFileCopy-binary.zip' + $zipHash = (Get-FileHash -Algorithm SHA256 -Path $zipPath).Hash.ToUpper() + $lines += "$zipHash QuickFileCopy-binary.zip" + $lines += "" + $lines += "# Binaries inside archive:" + $binFiles = @('dist/QuickFileCopy.exe', 'dist/QuickFileCopy_cli.exe', 'dist/WebView2Loader.dll') + foreach ($f in $binFiles) { + if (Test-Path -LiteralPath $f) { + $h = (Get-FileHash -Algorithm SHA256 -Path $f).Hash.ToUpper() + $name = Split-Path -Leaf $f + $lines += "$h $name" + } + } + $lines | Set-Content -Path $checksumFile -Encoding utf8 + - name: Upload release asset uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} name: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} - files: QuickFileCopy-binary.zip + files: | + QuickFileCopy-binary.zip + SHA256SUMS.txt overwrite_files: true generate_release_notes: true body: | diff --git a/.gitignore b/.gitignore index 61ce5e7..2244083 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,14 @@ -# Python and test caches +# Build output & intermediate files +build/ +dist/* +!dist/.gitkeep +*.o +*.exe +*.dll +*.log +*_debug.log + +# Python & virtual environments __pycache__/ *.py[cod] *$py.class @@ -9,34 +19,16 @@ env/ *.egg-info/ .env -# Editors and local tools +# Editors & local tools .vscode/ .idea/ .claude/ +CODEX_PROJECT_NOTE.txt -# C++ and CMake outputs -build/ -core/native/*.dll -core/native/*.o -core/native/resources/QuickFileCopy.html - -# Distribution binaries are generated; distribution documents are retained. -dist/binary/* -!dist/binary/.gitkeep -dist/*.zip - -# Benchmark data generated on local machines -python/benchmark/testdata/ -python/benchmark/**/results/*.json - -# Prototype build outputs -python/prototype/*.dist/ -python/prototype/*.build/ -python/prototype/*.onefile-build/ - -# Reference repositories and logs -reference/ -*.log +# Benchmark & test data +proto/benchmark/testdata/ +proto/benchmark/**/results/*.json +proto/prototype/*.dist/ +proto/prototype/*.build/ +proto/prototype/*.onefile-build/ *.dat -*_debug.log -CODEX_PROJECT_NOTE.txt diff --git a/README.md b/README.md index 476b73b..f79de50 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,14 @@ Extract every file into the same folder and run `QuickFileCopy.exe`. - `history.txt` / `history_jp.txt` - change log - `LICENSE.txt` / `LICENSE_jp.txt` - license files +### Integrity verification (SHA-256) + +Official SHA-256 checksums for the distribution ZIP and binaries are automatically computed during the CI (GitHub Actions) build and published as `SHA256SUMS.txt` on each release page. Verify the downloaded package with PowerShell: + +```powershell +Get-FileHash .\QuickFileCopy-binary.zip -Algorithm SHA256 +``` + The GUI HTML is embedded in `QuickFileCopy.exe`; an external `index.html` is not required. ## GUI usage @@ -57,9 +65,9 @@ Complete-preservation modes launch a separate elevated worker through UAC. The m ## CLI usage ```powershell -.\dist\binary\QuickFileCopy_cli.exe --help -.\dist\binary\QuickFileCopy_cli.exe copy C:\Source D:\Backup --contents --policy newer -.\dist\binary\QuickFileCopy_cli.exe copy C:\Source D:\Backup --folder --policy overwrite --verify +.\dist\QuickFileCopy_cli.exe --help +.\dist\QuickFileCopy_cli.exe copy C:\Source D:\Backup --contents --policy newer +.\dist\QuickFileCopy_cli.exe copy C:\Source D:\Backup --folder --policy overwrite --verify ``` ## Build @@ -73,33 +81,37 @@ Requirements: ```powershell winget install --id BrechtSanders.WinLibs.MCF.UCRT --exact --source winget -build.bat +scripts\build.bat ``` The default WebView2 SDK root is `C:\tools\webview2\build\native`. Override it with `WEBVIEW2_ROOT`, `WEBVIEW2_INCLUDE`, or `WEBVIEW2_LOADER`. -Build outputs are written to `dist\binary`. Run the smoke tests after building: +Build outputs are written to `dist/`. Run the smoke tests after building: ```powershell -python python\tests\native_smoke.py +python proto\tests\native_smoke.py ``` -Detailed specifications and build notes are in [`document/`](document/). +Detailed specifications and build notes are in [`docs/`](docs/). ## Project layout ```text -core/native/ C++ engine, GUI host, CLI, and resources -python/prototype/ archived Python/PySide6 prototype -python/tests/ native CLI smoke and benchmark runners -python/benchmark/ historical Python prototype measurements -templates/ self-contained WebView2 development UI -static/ reserved CSS/JS/image source area -assets/ icon sources and future public screenshots -document/ specification, environment, and version information -plans/ dated implementation plans and results -dist/binary/ generated binaries, excluded from source control -dist/documents/ user-facing distribution documents +QuickFileCopy/ +├── src/ +│ ├── app/ GUI host & Windows resources (main_gui.cpp, .rc, .ico, resource.h) +│ ├── cli/ CLI entry point (main_cli.cpp) +│ ├── engine/ Copy engine and qfc/ header (copy_engine.cpp, qfc/copy_engine.h) +│ └── ui/ UI source files (index.html, css/, js/, img/) +├── proto/ +│ ├── prototype/ archived Python/PySide6 prototype +│ ├── tests/ native CLI smoke and benchmark runners +│ └── benchmark/ historical Python prototype measurements +├── scripts/ build.py, build.bat, bundle_html.py +├── docs/ specification, environment, and version information +│ └── distribution/ user-facing distribution documents +├── dist/ generated binaries, excluded from source control (except .gitkeep) +└── .github/workflows/ CI and release workflows ``` ## License and disclaimer diff --git a/README_jp.md b/README_jp.md index e8f3c83..6c42c7a 100644 --- a/README_jp.md +++ b/README_jp.md @@ -42,6 +42,14 @@ GUIと文書は日本語/Englishに対応し、QDB形式の言語ボタンで - `history.txt` / `history_jp.txt` - 更新履歴 - `LICENSE.txt` / `LICENSE_jp.txt` - ライセンス +### 完全性検証 (SHA-256) + +配布用 ZIP および各バイナリの公式 SHA-256 ハッシュ値は、CI (GitHub Actions) ビルド時に自動計算され、各リリースページに `SHA256SUMS.txt` として添付・公開されています。PowerShell でダウンロードファイルの完全性を確認できます: + +```powershell +Get-FileHash .\QuickFileCopy-binary.zip -Algorithm SHA256 +``` + GUIのHTMLは`QuickFileCopy.exe`へ埋め込まれるため、外部の`index.html`は不要です。 ## GUIの使い方 @@ -57,9 +65,9 @@ GUIのHTMLは`QuickFileCopy.exe`へ埋め込まれるため、外部の`index.ht ## CLIの使い方 ```powershell -.\dist\binary\QuickFileCopy_cli.exe --help -.\dist\binary\QuickFileCopy_cli.exe copy C:\Source D:\Backup --contents --policy newer -.\dist\binary\QuickFileCopy_cli.exe copy C:\Source D:\Backup --folder --policy overwrite --verify +.\dist\QuickFileCopy_cli.exe --help +.\dist\QuickFileCopy_cli.exe copy C:\Source D:\Backup --contents --policy newer +.\dist\QuickFileCopy_cli.exe copy C:\Source D:\Backup --folder --policy overwrite --verify ``` ## ビルド @@ -73,33 +81,37 @@ GUIのHTMLは`QuickFileCopy.exe`へ埋め込まれるため、外部の`index.ht ```powershell winget install --id BrechtSanders.WinLibs.MCF.UCRT --exact --source winget -build.bat +scripts\build.bat ``` WebView2 SDKの既定ルートは`C:\tools\webview2\build\native`です。別の場所を使う場合は`WEBVIEW2_ROOT`、`WEBVIEW2_INCLUDE`、`WEBVIEW2_LOADER`で指定できます。 -成果物は`dist\binary`へ出力されます。ビルド後のスモークテスト: +成果物は`dist/`へ出力されます。ビルド後のスモークテスト: ```powershell -python python\tests\native_smoke.py +python proto\tests\native_smoke.py ``` -詳しい仕様とビルド情報は[`document/`](document/)にあります。 +詳しい仕様とビルド情報は[`docs/`](docs/)にあります。 ## フォルダ構成 ```text -core/native/ C++エンジン、GUIホスト、CLI、リソース -python/prototype/ 旧Python/PySide6プロトタイプ -python/tests/ ネイティブCLIのスモークとベンチマーク -python/benchmark/ 旧Python試作の計測記録 -templates/ 自己完結WebView2開発用UI -static/ CSS/JS/画像を分離する場合の配置先 -assets/ アイコン原本と今後の公開用スクリーンショット -document/ 仕様、開発環境、バージョン情報 -plans/ 日付付きの計画書と実施結果 -dist/binary/ 生成バイナリ(ソース管理対象外) -dist/documents/ 配布用ユーザー文書 +QuickFileCopy/ +├── src/ +│ ├── app/ GUIホスト & Windowsリソース(main_gui.cpp, .rc, .ico, resource.h) +│ ├── cli/ CLIエントリーポイント(main_cli.cpp) +│ ├── engine/ コピーエンジンおよびqfc/ヘッダー(copy_engine.cpp, qfc/copy_engine.h) +│ └── ui/ UIソース(index.html, css/, js/, img/) +├── proto/ +│ ├── prototype/ 旧Python/PySide6プロトタイプ +│ ├── tests/ ネイティブCLIのスモークとベンチマーク +│ └── benchmark/ 旧Python試作の計測記録 +├── scripts/ build.py, build.bat, bundle_html.py +├── docs/ 仕様、開発環境、バージョン情報 +│ └── distribution/ 配布用ユーザー文書 +├── dist/ 生成バイナリ(ソース管理対象外、.gitkeepのみ保持) +└── .github/workflows/ CI / Release ワークフロー ``` ## ライセンスと免責 diff --git a/build.bat b/build.bat deleted file mode 100644 index 8cf9927..0000000 --- a/build.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -setlocal -cd /d "%~dp0" -python build_native.py -exit /b %ERRORLEVEL% diff --git a/core/native/CMakeLists.txt b/core/native/CMakeLists.txt deleted file mode 100644 index 53d3434..0000000 --- a/core/native/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -project(QuickFileCopyNative VERSION 1.0.0 LANGUAGES CXX RC) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if(NOT WIN32) - message(FATAL_ERROR "QuickFileCopy native is Windows-only") -endif() - -set(WEBVIEW2_INCLUDE "C:/tools/webview2/build/native/include" CACHE PATH "WebView2 SDK include directory") -set(QFC_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/resources/QuickFileCopy.ico") -set(QFC_HTML_PATH "${CMAKE_CURRENT_SOURCE_DIR}/resources/QuickFileCopy.html") -configure_file( - resources/QuickFileCopy.rc.in - "${CMAKE_CURRENT_BINARY_DIR}/QuickFileCopy.rc" - @ONLY -) - -add_library(qfc_core STATIC src/copy_engine.cpp) -target_include_directories(qfc_core PUBLIC include) -target_compile_definitions(qfc_core PUBLIC UNICODE _UNICODE WINVER=0x0A00 _WIN32_WINNT=0x0A00) - -add_executable(QuickFileCopy_cli src/main_cli.cpp) -target_link_libraries(QuickFileCopy_cli PRIVATE qfc_core kernel32 advapi32 bcrypt) -if(MINGW) - target_link_options(QuickFileCopy_cli PRIVATE -municode) -endif() - -add_executable(QuickFileCopy WIN32 - src/webview_main.cpp - "${CMAKE_CURRENT_BINARY_DIR}/QuickFileCopy.rc" -) -target_include_directories(QuickFileCopy PRIVATE "${WEBVIEW2_INCLUDE}" resources) -target_compile_definitions(QuickFileCopy PRIVATE UNICODE _UNICODE WINVER=0x0A00 _WIN32_WINNT=0x0A00) -target_link_libraries(QuickFileCopy PRIVATE qfc_core kernel32 user32 dwmapi ole32 uuid shell32 advapi32 bcrypt) - -if(MINGW) - foreach(target QuickFileCopy_cli QuickFileCopy) - target_compile_options(${target} PRIVATE -O3 -ffunction-sections -fdata-sections) - target_link_options(${target} PRIVATE -static -static-libgcc -static-libstdc++ -Wl,--gc-sections) - endforeach() -endif() diff --git a/core/native/README.md b/core/native/README.md deleted file mode 100644 index cf247b0..0000000 --- a/core/native/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# QuickFileCopy native implementation - -This directory contains the production C++20 implementation. It does not link to or embed the archived Python prototype. - -## Files - -```text -include/qfc/copy_engine.h public copy-engine API -src/copy_engine.cpp scanner, queue, copy paths, verification, metadata -src/webview_main.cpp Win32 window, WebView2 host, UAC worker, WebMessage -src/main_cli.cpp command-line application -resources/ resource script, VERSIONINFO, generated embedded HTML -``` - -Python smoke tests live in `python/tests/`. The standard build entry point is the repository-root `build.bat` or `build_native.py`. - -```powershell -build.bat -python python\tests\native_smoke.py -``` - -Outputs: - -```text -dist/binary/QuickFileCopy.exe -dist/binary/QuickFileCopy_cli.exe -dist/binary/WebView2Loader.dll -``` - -The GUI HTML is generated from `templates/index.html` and embedded into the executable. It is not a separate runtime file. - -See `document/spec.md` for behavior and `document/environment.md` for the complete build environment. diff --git a/core/native/resources/QuickFileCopy.rc.in b/core/native/resources/QuickFileCopy.rc.in deleted file mode 100644 index 60be5ce..0000000 --- a/core/native/resources/QuickFileCopy.rc.in +++ /dev/null @@ -1,33 +0,0 @@ -#include "@CMAKE_CURRENT_SOURCE_DIR@/resources/resource.h" - -IDI_APP_ICON ICON "@QFC_ICON_PATH@" -IDR_APP_HTML RCDATA "@QFC_HTML_PATH@" - -1 VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x3fL - FILEFLAGS 0x0L - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "041104B0" - BEGIN - VALUE "CompanyName", "QuickFileCopy Project\0" - VALUE "FileDescription", "QuickFileCopy - Native High-Speed File Copy\0" - VALUE "FileVersion", "1.0.0\0" - VALUE "InternalName", "QuickFileCopy\0" - VALUE "LegalCopyright", "Copyright (C) 2026 QuickFileCopy contributors\0" - VALUE "OriginalFilename", "QuickFileCopy.exe\0" - VALUE "ProductName", "QuickFileCopy\0" - VALUE "ProductVersion", "1.0.0\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0411, 1200 - END -END diff --git a/dist/binary/.gitkeep b/dist/.gitkeep similarity index 100% rename from dist/binary/.gitkeep rename to dist/.gitkeep diff --git a/document/about.md b/docs/about.md similarity index 100% rename from document/about.md rename to docs/about.md diff --git a/document/about_jp.md b/docs/about_jp.md similarity index 100% rename from document/about_jp.md rename to docs/about_jp.md diff --git a/dist/documents/LICENSE.txt b/docs/distribution/LICENSE.txt similarity index 100% rename from dist/documents/LICENSE.txt rename to docs/distribution/LICENSE.txt diff --git a/dist/documents/LICENSE_jp.txt b/docs/distribution/LICENSE_jp.txt similarity index 100% rename from dist/documents/LICENSE_jp.txt rename to docs/distribution/LICENSE_jp.txt diff --git a/dist/documents/history.txt b/docs/distribution/history.txt similarity index 100% rename from dist/documents/history.txt rename to docs/distribution/history.txt diff --git a/dist/documents/history_jp.txt b/docs/distribution/history_jp.txt similarity index 100% rename from dist/documents/history_jp.txt rename to docs/distribution/history_jp.txt diff --git a/dist/documents/readme.txt b/docs/distribution/readme.txt similarity index 88% rename from dist/documents/readme.txt rename to docs/distribution/readme.txt index 950e011..84727cc 100644 --- a/dist/documents/readme.txt +++ b/docs/distribution/readme.txt @@ -43,10 +43,12 @@ Distribution files - history.txt / history_jp.txt - change log - LICENSE.txt / LICENSE_jp.txt - MIT License files -SHA-256 -------- -DD5CB93CF84CD6FE5D0DF78DC1A640B3CF97EF4E153989CC8F39A7EF681651E7 QuickFileCopy.exe -5CAE1046B51B5D60EB929561294E3E2F526C62CAA97A828BA9B326F693E08843 QuickFileCopy_cli.exe +Integrity verification (SHA-256) +-------------------------------- +Official SHA-256 checksums are automatically generated by the CI build and +published as SHA256SUMS.txt on each GitHub Release page. Verify with PowerShell: + Get-FileHash .\QuickFileCopy-binary.zip -Algorithm SHA256 + License ------- diff --git a/dist/documents/readme_jp.txt b/docs/distribution/readme_jp.txt similarity index 86% rename from dist/documents/readme_jp.txt rename to docs/distribution/readme_jp.txt index f728cf8..ebc00b9 100644 --- a/dist/documents/readme_jp.txt +++ b/docs/distribution/readme_jp.txt @@ -43,10 +43,12 @@ QuickFileCopy_cli.exe --help - history.txt / history_jp.txt - 更新履歴 - LICENSE.txt / LICENSE_jp.txt - MIT License -SHA-256 -------- -DD5CB93CF84CD6FE5D0DF78DC1A640B3CF97EF4E153989CC8F39A7EF681651E7 QuickFileCopy.exe -5CAE1046B51B5D60EB929561294E3E2F526C62CAA97A828BA9B326F693E08843 QuickFileCopy_cli.exe +完全性検証(SHA-256) +--------------------- +配布用ZIPおよびバイナリの公式SHA-256ハッシュ値はCIビルド時に自動計算され、 +GitHub Releasesの各リリースに SHA256SUMS.txt として添付・公開されています。 +PowerShellでダウンロードファイルの整合性を確認できます: + Get-FileHash .\QuickFileCopy-binary.zip -Algorithm SHA256 ライセンス ---------- diff --git a/document/environment.md b/docs/environment.md similarity index 100% rename from document/environment.md rename to docs/environment.md diff --git a/document/environment_jp.md b/docs/environment_jp.md similarity index 100% rename from document/environment_jp.md rename to docs/environment_jp.md diff --git a/document/spec.md b/docs/spec.md similarity index 100% rename from document/spec.md rename to docs/spec.md diff --git a/document/spec_jp.md b/docs/spec_jp.md similarity index 100% rename from document/spec_jp.md rename to docs/spec_jp.md diff --git a/python/benchmark/README.md b/proto/benchmark/README.md similarity index 100% rename from python/benchmark/README.md rename to proto/benchmark/README.md diff --git a/python/benchmark/generate_dataset.py b/proto/benchmark/generate_dataset.py similarity index 100% rename from python/benchmark/generate_dataset.py rename to proto/benchmark/generate_dataset.py diff --git a/python/benchmark/results/fastcopy_comparison.md b/proto/benchmark/results/fastcopy_comparison.md similarity index 100% rename from python/benchmark/results/fastcopy_comparison.md rename to proto/benchmark/results/fastcopy_comparison.md diff --git a/python/benchmark/results/final_comparison.md b/proto/benchmark/results/final_comparison.md similarity index 100% rename from python/benchmark/results/final_comparison.md rename to proto/benchmark/results/final_comparison.md diff --git a/python/benchmark/results/large_files_benchmark.md b/proto/benchmark/results/large_files_benchmark.md similarity index 100% rename from python/benchmark/results/large_files_benchmark.md rename to proto/benchmark/results/large_files_benchmark.md diff --git a/python/benchmark/results/small_files_benchmark.md b/proto/benchmark/results/small_files_benchmark.md similarity index 100% rename from python/benchmark/results/small_files_benchmark.md rename to proto/benchmark/results/small_files_benchmark.md diff --git a/python/benchmark/run_benchmark.py b/proto/benchmark/run_benchmark.py similarity index 100% rename from python/benchmark/run_benchmark.py rename to proto/benchmark/run_benchmark.py diff --git a/python/prototype/build_exe.ps1 b/proto/prototype/build_exe.ps1 similarity index 100% rename from python/prototype/build_exe.ps1 rename to proto/prototype/build_exe.ps1 diff --git a/python/prototype/nuitka-crash-report.xml b/proto/prototype/nuitka-crash-report.xml similarity index 100% rename from python/prototype/nuitka-crash-report.xml rename to proto/prototype/nuitka-crash-report.xml diff --git a/python/prototype/pyproject.toml b/proto/prototype/pyproject.toml similarity index 100% rename from python/prototype/pyproject.toml rename to proto/prototype/pyproject.toml diff --git a/python/prototype/requirements.txt b/proto/prototype/requirements.txt similarity index 100% rename from python/prototype/requirements.txt rename to proto/prototype/requirements.txt diff --git a/python/prototype/run_cli.py b/proto/prototype/run_cli.py similarity index 100% rename from python/prototype/run_cli.py rename to proto/prototype/run_cli.py diff --git a/python/prototype/run_gui.py b/proto/prototype/run_gui.py similarity index 100% rename from python/prototype/run_gui.py rename to proto/prototype/run_gui.py diff --git a/python/prototype/src/fastestcopy/__init__.py b/proto/prototype/src/fastestcopy/__init__.py similarity index 100% rename from python/prototype/src/fastestcopy/__init__.py rename to proto/prototype/src/fastestcopy/__init__.py diff --git a/python/prototype/src/fastestcopy/cli.py b/proto/prototype/src/fastestcopy/cli.py similarity index 100% rename from python/prototype/src/fastestcopy/cli.py rename to proto/prototype/src/fastestcopy/cli.py diff --git a/python/prototype/src/fastestcopy/engine/__init__.py b/proto/prototype/src/fastestcopy/engine/__init__.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/__init__.py rename to proto/prototype/src/fastestcopy/engine/__init__.py diff --git a/python/prototype/src/fastestcopy/engine/copier.py b/proto/prototype/src/fastestcopy/engine/copier.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/copier.py rename to proto/prototype/src/fastestcopy/engine/copier.py diff --git a/python/prototype/src/fastestcopy/engine/diskspace.py b/proto/prototype/src/fastestcopy/engine/diskspace.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/diskspace.py rename to proto/prototype/src/fastestcopy/engine/diskspace.py diff --git a/python/prototype/src/fastestcopy/engine/planner.py b/proto/prototype/src/fastestcopy/engine/planner.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/planner.py rename to proto/prototype/src/fastestcopy/engine/planner.py diff --git a/python/prototype/src/fastestcopy/engine/policy.py b/proto/prototype/src/fastestcopy/engine/policy.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/policy.py rename to proto/prototype/src/fastestcopy/engine/policy.py diff --git a/python/prototype/src/fastestcopy/engine/preview.py b/proto/prototype/src/fastestcopy/engine/preview.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/preview.py rename to proto/prototype/src/fastestcopy/engine/preview.py diff --git a/python/prototype/src/fastestcopy/engine/scanner.py b/proto/prototype/src/fastestcopy/engine/scanner.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/scanner.py rename to proto/prototype/src/fastestcopy/engine/scanner.py diff --git a/python/prototype/src/fastestcopy/engine/stats.py b/proto/prototype/src/fastestcopy/engine/stats.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/stats.py rename to proto/prototype/src/fastestcopy/engine/stats.py diff --git a/python/prototype/src/fastestcopy/engine/winio.py b/proto/prototype/src/fastestcopy/engine/winio.py similarity index 100% rename from python/prototype/src/fastestcopy/engine/winio.py rename to proto/prototype/src/fastestcopy/engine/winio.py diff --git a/python/prototype/src/fastestcopy/gui/__init__.py b/proto/prototype/src/fastestcopy/gui/__init__.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/__init__.py rename to proto/prototype/src/fastestcopy/gui/__init__.py diff --git a/python/prototype/src/fastestcopy/gui/app.py b/proto/prototype/src/fastestcopy/gui/app.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/app.py rename to proto/prototype/src/fastestcopy/gui/app.py diff --git a/python/prototype/src/fastestcopy/gui/copy_dialog.py b/proto/prototype/src/fastestcopy/gui/copy_dialog.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/copy_dialog.py rename to proto/prototype/src/fastestcopy/gui/copy_dialog.py diff --git a/python/prototype/src/fastestcopy/gui/copy_log.py b/proto/prototype/src/fastestcopy/gui/copy_log.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/copy_log.py rename to proto/prototype/src/fastestcopy/gui/copy_log.py diff --git a/python/prototype/src/fastestcopy/gui/drives.py b/proto/prototype/src/fastestcopy/gui/drives.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/drives.py rename to proto/prototype/src/fastestcopy/gui/drives.py diff --git a/python/prototype/src/fastestcopy/gui/elevate.py b/proto/prototype/src/fastestcopy/gui/elevate.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/elevate.py rename to proto/prototype/src/fastestcopy/gui/elevate.py diff --git a/python/prototype/src/fastestcopy/gui/file_pane.py b/proto/prototype/src/fastestcopy/gui/file_pane.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/file_pane.py rename to proto/prototype/src/fastestcopy/gui/file_pane.py diff --git a/python/prototype/src/fastestcopy/gui/help_content.py b/proto/prototype/src/fastestcopy/gui/help_content.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/help_content.py rename to proto/prototype/src/fastestcopy/gui/help_content.py diff --git a/python/prototype/src/fastestcopy/gui/help_dialog.py b/proto/prototype/src/fastestcopy/gui/help_dialog.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/help_dialog.py rename to proto/prototype/src/fastestcopy/gui/help_dialog.py diff --git a/python/prototype/src/fastestcopy/gui/i18n.py b/proto/prototype/src/fastestcopy/gui/i18n.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/i18n.py rename to proto/prototype/src/fastestcopy/gui/i18n.py diff --git a/python/prototype/src/fastestcopy/gui/main_window.py b/proto/prototype/src/fastestcopy/gui/main_window.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/main_window.py rename to proto/prototype/src/fastestcopy/gui/main_window.py diff --git a/python/prototype/src/fastestcopy/gui/netbrowse.py b/proto/prototype/src/fastestcopy/gui/netbrowse.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/netbrowse.py rename to proto/prototype/src/fastestcopy/gui/netbrowse.py diff --git a/python/prototype/src/fastestcopy/gui/settings_dialog.py b/proto/prototype/src/fastestcopy/gui/settings_dialog.py similarity index 100% rename from python/prototype/src/fastestcopy/gui/settings_dialog.py rename to proto/prototype/src/fastestcopy/gui/settings_dialog.py diff --git a/python/prototype/tests/test_elevate.py b/proto/prototype/tests/test_elevate.py similarity index 100% rename from python/prototype/tests/test_elevate.py rename to proto/prototype/tests/test_elevate.py diff --git a/python/prototype/tests/test_engine.py b/proto/prototype/tests/test_engine.py similarity index 100% rename from python/prototype/tests/test_engine.py rename to proto/prototype/tests/test_engine.py diff --git a/python/prototype/tests/test_file_pane.py b/proto/prototype/tests/test_file_pane.py similarity index 100% rename from python/prototype/tests/test_file_pane.py rename to proto/prototype/tests/test_file_pane.py diff --git a/python/prototype/tests/test_i18n.py b/proto/prototype/tests/test_i18n.py similarity index 100% rename from python/prototype/tests/test_i18n.py rename to proto/prototype/tests/test_i18n.py diff --git a/python/prototype/tests/test_main_window.py b/proto/prototype/tests/test_main_window.py similarity index 100% rename from python/prototype/tests/test_main_window.py rename to proto/prototype/tests/test_main_window.py diff --git a/python/tests/benchmark_native.py b/proto/tests/benchmark_native.py similarity index 100% rename from python/tests/benchmark_native.py rename to proto/tests/benchmark_native.py diff --git a/python/tests/native_smoke.py b/proto/tests/native_smoke.py similarity index 98% rename from python/tests/native_smoke.py rename to proto/tests/native_smoke.py index d69e18f..b91d6e6 100644 --- a/python/tests/native_smoke.py +++ b/proto/tests/native_smoke.py @@ -9,7 +9,7 @@ ROOT = Path(__file__).resolve().parents[2] -QFC = ROOT / "dist" / "binary" / "QuickFileCopy_cli.exe" +QFC = ROOT / "dist" / "QuickFileCopy_cli.exe" def digest(path: Path) -> str: diff --git a/scripts/build.bat b/scripts/build.bat new file mode 100644 index 0000000..4ebb7e7 --- /dev/null +++ b/scripts/build.bat @@ -0,0 +1,6 @@ +@echo off +setlocal +cd /d "%~dp0\.." +echo [Build] Building QuickFileCopy... +python scripts\build.py %* + diff --git a/build_native.py b/scripts/build.py similarity index 71% rename from build_native.py rename to scripts/build.py index f3ebbb2..c72c86a 100644 --- a/build_native.py +++ b/scripts/build.py @@ -7,10 +7,21 @@ import subprocess import sys +SCRIPT_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPT_DIR.parent +SRC = REPO_ROOT / "src" +APP_DIR = SRC / "app" +CLI_DIR = SRC / "cli" +ENGINE_DIR = SRC / "engine" +INTERMEDIATE = REPO_ROOT / "build" / "intermediate" +DIST = REPO_ROOT / "dist" ROOT = Path(__file__).resolve().parent NATIVE = ROOT / "core" / "native" DIST = ROOT / "dist" / "binary" +if str(SCRIPT_DIR) not in sys.path: + sys.path.insert(0, str(SCRIPT_DIR)) +import bundle_html def find_tool(names: tuple[str, ...]) -> Path | None: @@ -39,8 +50,10 @@ def find_tool(names: tuple[str, ...]) -> Path | None: def run(command: list[str], label: str) -> None: +def run(command: list[str], label: str, cwd: Path | None = None) -> None: print(f"\n[{label}] {' '.join(command)}") completed = subprocess.run(command, cwd=ROOT, text=True) + completed = subprocess.run(command, cwd=cwd or REPO_ROOT, text=True) if completed.returncode: raise SystemExit(completed.returncode) @@ -60,8 +73,15 @@ def build() -> None: raise SystemExit(f"WebView2Loader.dll was not found: {webview_loader}") run([sys.executable, str(ROOT / "bundle_html.py")], "bundle-html") + INTERMEDIATE.mkdir(parents=True, exist_ok=True) DIST.mkdir(parents=True, exist_ok=True) resource_object = DIST / "QuickFileCopy_res.o" + + # 1. Bundle HTML into build/intermediate/QuickFileCopy.html + bundled_html = INTERMEDIATE / "QuickFileCopy.html" + bundle_html.bundle(bundled_html) + + resource_object = INTERMEDIATE / "QuickFileCopy_res.o" common = [ str(compiler), "-O3", @@ -77,14 +97,20 @@ def build() -> None: "-static-libstdc++", "-Wl,--gc-sections", f"-I{NATIVE / 'include'}", + f"-I{SRC}", + f"-I{ENGINE_DIR}", + f"-I{APP_DIR}", ] + # 2. CLI Build run( common + [ "-municode", str(NATIVE / "src" / "copy_engine.cpp"), str(NATIVE / "src" / "main_cli.cpp"), + str(ENGINE_DIR / "copy_engine.cpp"), + str(CLI_DIR / "main_cli.cpp"), "-o", str(DIST / "QuickFileCopy_cli.exe"), "-lkernel32", @@ -93,10 +119,25 @@ def build() -> None: ], "cli", ) + + # 3. Resource compilation (include APP_DIR and INTERMEDIATE for QuickFileCopy.html and .ico) run( [str(windres), str(NATIVE / "resources" / "QuickFileCopy.rc"), "-O", "coff", "-o", str(resource_object)], + [ + str(windres), + f"-I{APP_DIR}", + f"-I{INTERMEDIATE}", + str(APP_DIR / "QuickFileCopy.rc"), + "-O", + "coff", + "-o", + str(resource_object), + ], "resource", + cwd=APP_DIR, ) + + # 4. GUI Build run( common + [ @@ -104,6 +145,8 @@ def build() -> None: f"-I{webview_include}", str(NATIVE / "src" / "copy_engine.cpp"), str(NATIVE / "src" / "webview_main.cpp"), + str(ENGINE_DIR / "copy_engine.cpp"), + str(APP_DIR / "main_gui.cpp"), str(resource_object), "-o", str(DIST / "QuickFileCopy.exe"), @@ -118,11 +161,14 @@ def build() -> None: ], "gui", ) + + # 5. Copy WebView2Loader.dll shutil.copy2(webview_loader, DIST / "WebView2Loader.dll") resource_object.unlink(missing_ok=True) for path in (DIST / "QuickFileCopy.exe", DIST / "QuickFileCopy_cli.exe", DIST / "WebView2Loader.dll"): print(f"[ok] {path.relative_to(ROOT)} ({path.stat().st_size:,} bytes)") + print(f"[ok] {path.relative_to(REPO_ROOT)} ({path.stat().st_size:,} bytes)") if __name__ == "__main__": diff --git a/bundle_html.py b/scripts/bundle_html.py similarity index 52% rename from bundle_html.py rename to scripts/bundle_html.py index 9c4f6d0..d4b7041 100644 --- a/bundle_html.py +++ b/scripts/bundle_html.py @@ -7,14 +7,28 @@ ROOT = Path(__file__).resolve().parent SOURCE = ROOT / "templates" / "index.html" OUTPUT = ROOT / "core" / "native" / "resources" / "QuickFileCopy.html" +SCRIPT_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPT_DIR.parent +SOURCE = REPO_ROOT / "src" / "ui" / "index.html" +OUTPUT = REPO_ROOT / "build" / "intermediate" / "QuickFileCopy.html" def main() -> None: +def bundle(output_path: Path | None = None) -> Path: + target = output_path or OUTPUT if not SOURCE.exists(): raise SystemExit(f"UI template was not found: {SOURCE}") OUTPUT.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(SOURCE, OUTPUT) print(f"[ok] bundled UI: {OUTPUT.relative_to(ROOT)}") + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(SOURCE, target) + print(f"[ok] bundled UI: {target.relative_to(REPO_ROOT)}") + return target + + +def main() -> None: + bundle() if __name__ == "__main__": diff --git a/core/native/resources/QuickFileCopy.ico b/src/app/QuickFileCopy.ico similarity index 100% rename from core/native/resources/QuickFileCopy.ico rename to src/app/QuickFileCopy.ico diff --git a/core/native/resources/QuickFileCopy.rc b/src/app/QuickFileCopy.rc similarity index 91% rename from core/native/resources/QuickFileCopy.rc rename to src/app/QuickFileCopy.rc index e6df9b0..6985695 100644 --- a/core/native/resources/QuickFileCopy.rc +++ b/src/app/QuickFileCopy.rc @@ -2,6 +2,8 @@ IDI_APP_ICON ICON "core/native/resources/QuickFileCopy.ico" IDR_APP_HTML RCDATA "core/native/resources/QuickFileCopy.html" +IDI_APP_ICON ICON "QuickFileCopy.ico" +IDR_APP_HTML RCDATA "QuickFileCopy.html" 1 VERSIONINFO FILEVERSION 1,0,0,0 diff --git a/core/native/src/webview_main.cpp b/src/app/main_gui.cpp similarity index 99% rename from core/native/src/webview_main.cpp rename to src/app/main_gui.cpp index 374c445..3110738 100644 --- a/core/native/src/webview_main.cpp +++ b/src/app/main_gui.cpp @@ -9,6 +9,7 @@ #include "qfc/copy_engine.h" #include "../resources/resource.h" +#include "resource.h" #include #include diff --git a/core/native/resources/resource.h b/src/app/resource.h similarity index 100% rename from core/native/resources/resource.h rename to src/app/resource.h diff --git a/core/native/src/main_cli.cpp b/src/cli/main_cli.cpp similarity index 100% rename from core/native/src/main_cli.cpp rename to src/cli/main_cli.cpp diff --git a/core/native/src/copy_engine.cpp b/src/engine/copy_engine.cpp similarity index 100% rename from core/native/src/copy_engine.cpp rename to src/engine/copy_engine.cpp diff --git a/core/native/include/qfc/copy_engine.h b/src/engine/qfc/copy_engine.h similarity index 100% rename from core/native/include/qfc/copy_engine.h rename to src/engine/qfc/copy_engine.h diff --git a/static/css/.gitkeep b/src/ui/css/.gitkeep similarity index 100% rename from static/css/.gitkeep rename to src/ui/css/.gitkeep diff --git a/static/img/.gitkeep b/src/ui/img/.gitkeep similarity index 100% rename from static/img/.gitkeep rename to src/ui/img/.gitkeep diff --git a/templates/index.html b/src/ui/index.html similarity index 100% rename from templates/index.html rename to src/ui/index.html diff --git a/static/js/.gitkeep b/src/ui/js/.gitkeep similarity index 100% rename from static/js/.gitkeep rename to src/ui/js/.gitkeep diff --git a/static/README.md b/static/README.md deleted file mode 100644 index b0b8fc8..0000000 --- a/static/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Static UI assets - -QuickFileCopy keeps its CSS and JavaScript inline in `templates/index.html` so the -WebView2 UI can be embedded as one resource. This directory is reserved for a future split -into `css/`, `js/`, and `img/`; `bundle_html.py` is the single bundling entry point.