├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── readme-chinese.md ├── sample ├── Cargo.toml ├── build.rs └── src │ └── main.rs ├── thunk-cli ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ ├── main.rs │ └── sys.rs └── thunk-rs ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "thunk-cli", 4 | "thunk-rs", 5 | "sample" 6 | ] 7 | resolver = "2" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 felixmaker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Use Thunk to build your Rust program to support old Windows platforms 2 | 3 | [中文自述文件](./readme-chinese.md) 4 | 5 | Thunk uses [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) and [YY-Thunks](https://github.com/Chuyu-Team/YY-Thunks) to build programs that support even Windows XP. So, how does it work? 6 | 7 | - Add VC-LTL to the library search path 8 | - Use YY-Thunks to remedy API that old platform that does not exist 9 | 10 | Note: Thunk does not guarantee the compiled program work or work accurately on old platforms. **USE AT YOUR OWN RISK**! 11 | 12 | ## Usage (As Command line tool) 13 | 14 | ## Preparation 15 | 16 | Download VC-LTL5 and YY-Thunks Binary, unzip them and add environment variable: 17 | 18 | | Binary | Environment Variable | 19 | | --- | ---| 20 | | VC-LTL-XXX-Binary.7z | VC_LTL | 21 | | YY-Thunks-XXX-Binary.zip | YY_THUNKS | 22 | 23 | Then add Thunk to run path. 24 | 25 | 26 | ## Install Thunk 27 | 28 | ``` 29 | cargo install thunk-cli 30 | ``` 31 | 32 | ## Sample 1. Build for Windows XP 33 | 34 | ``` 35 | cargo new build_for_xp 36 | cd build_for_xp 37 | thunk --os xp --arch x86 -- --release 38 | ``` 39 | 40 | ## Sample 2. Build a shared library for Windows XP 41 | 42 | ``` 43 | cargo new build_for_xp 44 | cd build_for_xp 45 | thunk --os xp --arch x86 --lib -- --release 46 | ``` 47 | 48 | ## Show help 49 | 50 | Use the following command to show help: 51 | 52 | ``` 53 | thunk.exe --help 54 | ``` 55 | 56 | Note: In order to distinguish the program build by Thunk, Thunk builds the release in `./target/*_build`. 57 | 58 | # Usage (As Library) 59 | 60 | Step1: Ensure command line tools `curl` and `7z` could be found in `PATH`. (Needed if `VC_LTL` and `YY_THUNKS` not found in environment variables) 61 | 62 | Step2: Add thunk as a build dependency: 63 | 64 | ``` 65 | cargo add thunk-rs --build 66 | ``` 67 | 68 | Step3: Create a build script `build.rs`: 69 | 70 | ``` 71 | fn main() { 72 | thunk::thunk(); 73 | } 74 | ``` 75 | 76 | Then, your program should run on Windows XP. See [thunk-rs](./thunk-rs/README.md). 77 | 78 | # Todo list 79 | 80 | - [x] Windows XP x86 81 | - [x] Windows XP x64 82 | - [x] Windows Vista x86 83 | - [x] Windows Vista x64 84 | - [x] Windows 7 x86 (v0.3.2) 85 | - [x] Windows 7 x64 (v0.3.2) 86 | - [x] Windows 8 x86 (v0.3.2) 87 | - [x] Windows 8 x64 (v0.3.2) 88 | - [x] Windows 10 x86 (v0.3.2) 89 | - [x] Windows 10 x64 (v0.3.2) 90 | - [x] Only VC-LTL 91 | - [] Scoop bucket 92 | 93 | 94 | # Thanks 95 | 96 | - [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) 97 | - [YY-Thunks](https://github.com/Chuyu-Team/YY-Thunks) 98 | -------------------------------------------------------------------------------- /readme-chinese.md: -------------------------------------------------------------------------------- 1 | # 使用 Thunk 帮助你编译能在 Windows XP 上运行的 Rust 程序 2 | 3 | Thunk 主要帮你做了下面两件事: 4 | 5 | - 将 [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) 添加到库搜索路径中 6 | - 额外链接 [YY-Thunks](https://github.com/Chuyu-Team/YY-Thunks),以弥补 Vista 和 XP 上没有的 API 7 | 8 | 注意:Thunk **并不能保证所编译的软件可以成功编译或者编译后正常运行**。 9 | 10 | # 作为命令行工具使用 11 | 12 | ## 准备工作 13 | 14 | 下载 VC-LTL5、 YY-Thunks Binary 文件,解压,并添加环境变量: 15 | 16 | | Binary | 环境变量 | 17 | | --- | ---| 18 | | VC-LTL-5.0.8-Beta2-Binary.7z | VC_LTL | 19 | | YY-Thunks-1.0.8-Beta4-Binary.zip | YY_THUNKS | 20 | 21 | ## 安装 22 | 23 | ``` 24 | cargo install thunk 25 | ``` 26 | 27 | ## 示例 1 编译一个可以在 XP 上运行的程序 28 | 29 | ``` 30 | cargo new build_for_xp 31 | cd build_for_xp 32 | thunk --os xp --arch x86 -- --release 33 | ``` 34 | 35 | ## 示例 2 编译一个可以在 XP 上使用的动态链接库 36 | 37 | ``` 38 | cargo new build_for_xp 39 | cd build_for_xp 40 | thunk --os xp --arch x86 --lib -- --release 41 | ``` 42 | 43 | ## 更多用法请查看帮助 44 | 45 | ``` 46 | thunk.exe --help 47 | ``` 48 | 49 | | 参数 | 说明 | 可能值 | 50 | | --- | --- | --- | 51 | | --os | 系统名称 | xp, vista, win7, win10, 20h1 | 52 | | --arch | 系统架构 | x86, x64, arm64 | 53 | | --lib | 是否为共享库,指定时 subsystem 将被忽略 | - | 54 | | --subsystem | 设置 subsystem | console, windows | 55 | | -- | -- 后面的参数会传到 cargo build 后面 | 自定义 | 56 | 57 | 注:为了区分 Thunk 编译出来的程序,程序生成在 `./target/*_build` 文件夹。 58 | 59 | # 作为类库使用 60 | 61 | 步骤1:确保 `curl` 和 `7z` 工具放置在运行目录里(如果设置了 `VC_LTL` 和 `YY_THUNKS` 环境变量可省去此步) 62 | 63 | 步骤2:添加 `thunk-rs` 依赖: 64 | 65 | ``` 66 | cargo add thunk-rs --build 67 | ``` 68 | 69 | 步骤3:添加生成脚本 `build.rs`: 70 | 71 | ``` 72 | fn main() { 73 | thunk::thunk(); 74 | } 75 | ``` 76 | 77 | 不出意外,编程出来程序可以在 XP 上运行,查看 [thunk-rs](./thunk-rs/README.md). 78 | 79 | 80 | # 任务清单 81 | 82 | - [x] Windows XP x86 83 | - [x] Windows XP x64 84 | - [x] Windows Vista x86 85 | - [x] Windows Vista x64 86 | - [x] Only VC-LTL 87 | - [x] Scoop bucket 88 | 89 | 90 | # 致谢 91 | 92 | - [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) 93 | - [YY-Thunks](https://github.com/Chuyu-Team/YY-Thunks) 94 | -------------------------------------------------------------------------------- /sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | thunk-rs = {path="../thunk-rs"} 8 | 9 | [features] 10 | -------------------------------------------------------------------------------- /sample/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | thunk::thunk(); 3 | } 4 | -------------------------------------------------------------------------------- /sample/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /thunk-cli/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is-terminal", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.0.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 40 | dependencies = [ 41 | "windows-sys", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" 49 | dependencies = [ 50 | "anstyle", 51 | "windows-sys", 52 | ] 53 | 54 | [[package]] 55 | name = "anyhow" 56 | version = "1.0.70" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" 59 | 60 | [[package]] 61 | name = "bitflags" 62 | version = "1.3.2" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 65 | 66 | [[package]] 67 | name = "cc" 68 | version = "1.0.79" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 71 | 72 | [[package]] 73 | name = "clap" 74 | version = "4.2.4" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" 77 | dependencies = [ 78 | "clap_builder", 79 | "clap_derive", 80 | "once_cell", 81 | ] 82 | 83 | [[package]] 84 | name = "clap_builder" 85 | version = "4.2.4" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" 88 | dependencies = [ 89 | "anstream", 90 | "anstyle", 91 | "bitflags", 92 | "clap_lex", 93 | "strsim", 94 | ] 95 | 96 | [[package]] 97 | name = "clap_derive" 98 | version = "4.2.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" 101 | dependencies = [ 102 | "heck", 103 | "proc-macro2", 104 | "quote", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "clap_lex" 110 | version = "0.4.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" 113 | 114 | [[package]] 115 | name = "colorchoice" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 119 | 120 | [[package]] 121 | name = "errno" 122 | version = "0.3.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 125 | dependencies = [ 126 | "errno-dragonfly", 127 | "libc", 128 | "windows-sys", 129 | ] 130 | 131 | [[package]] 132 | name = "errno-dragonfly" 133 | version = "0.1.2" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 136 | dependencies = [ 137 | "cc", 138 | "libc", 139 | ] 140 | 141 | [[package]] 142 | name = "heck" 143 | version = "0.4.1" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 146 | 147 | [[package]] 148 | name = "hermit-abi" 149 | version = "0.3.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 152 | 153 | [[package]] 154 | name = "io-lifetimes" 155 | version = "1.0.10" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" 158 | dependencies = [ 159 | "hermit-abi", 160 | "libc", 161 | "windows-sys", 162 | ] 163 | 164 | [[package]] 165 | name = "is-terminal" 166 | version = "0.4.7" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 169 | dependencies = [ 170 | "hermit-abi", 171 | "io-lifetimes", 172 | "rustix", 173 | "windows-sys", 174 | ] 175 | 176 | [[package]] 177 | name = "libc" 178 | version = "0.2.142" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" 181 | 182 | [[package]] 183 | name = "linux-raw-sys" 184 | version = "0.3.4" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" 187 | 188 | [[package]] 189 | name = "once_cell" 190 | version = "1.17.1" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 193 | 194 | [[package]] 195 | name = "proc-macro2" 196 | version = "1.0.56" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 199 | dependencies = [ 200 | "unicode-ident", 201 | ] 202 | 203 | [[package]] 204 | name = "quote" 205 | version = "1.0.26" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 208 | dependencies = [ 209 | "proc-macro2", 210 | ] 211 | 212 | [[package]] 213 | name = "rustix" 214 | version = "0.37.14" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" 217 | dependencies = [ 218 | "bitflags", 219 | "errno", 220 | "io-lifetimes", 221 | "libc", 222 | "linux-raw-sys", 223 | "windows-sys", 224 | ] 225 | 226 | [[package]] 227 | name = "strsim" 228 | version = "0.10.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 231 | 232 | [[package]] 233 | name = "syn" 234 | version = "2.0.15" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 237 | dependencies = [ 238 | "proc-macro2", 239 | "quote", 240 | "unicode-ident", 241 | ] 242 | 243 | [[package]] 244 | name = "thunk" 245 | version = "0.2.3" 246 | dependencies = [ 247 | "anyhow", 248 | "clap", 249 | ] 250 | 251 | [[package]] 252 | name = "unicode-ident" 253 | version = "1.0.8" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 256 | 257 | [[package]] 258 | name = "utf8parse" 259 | version = "0.2.1" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 262 | 263 | [[package]] 264 | name = "windows-sys" 265 | version = "0.48.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 268 | dependencies = [ 269 | "windows-targets", 270 | ] 271 | 272 | [[package]] 273 | name = "windows-targets" 274 | version = "0.48.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 277 | dependencies = [ 278 | "windows_aarch64_gnullvm", 279 | "windows_aarch64_msvc", 280 | "windows_i686_gnu", 281 | "windows_i686_msvc", 282 | "windows_x86_64_gnu", 283 | "windows_x86_64_gnullvm", 284 | "windows_x86_64_msvc", 285 | ] 286 | 287 | [[package]] 288 | name = "windows_aarch64_gnullvm" 289 | version = "0.48.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 292 | 293 | [[package]] 294 | name = "windows_aarch64_msvc" 295 | version = "0.48.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 298 | 299 | [[package]] 300 | name = "windows_i686_gnu" 301 | version = "0.48.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 304 | 305 | [[package]] 306 | name = "windows_i686_msvc" 307 | version = "0.48.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 310 | 311 | [[package]] 312 | name = "windows_x86_64_gnu" 313 | version = "0.48.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 316 | 317 | [[package]] 318 | name = "windows_x86_64_gnullvm" 319 | version = "0.48.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 322 | 323 | [[package]] 324 | name = "windows_x86_64_msvc" 325 | version = "0.48.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 328 | -------------------------------------------------------------------------------- /thunk-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thunk-cli" 3 | version = "0.3.3" 4 | edition = "2021" 5 | authors = ["felixmaker"] 6 | description = "Thunk the Rust program to support Windows XP, Vista and more!" 7 | license = "Apache-2.0 OR MIT" 8 | keywords = ["build-dependencies"] 9 | readme = "README.md" 10 | repository = "https://github.com/felixmaker/thunk" 11 | documentation = "https://docs.rs/thunk-rs" 12 | 13 | [[bin]] 14 | path = "src/main.rs" 15 | name = "thunk" 16 | 17 | [dependencies] 18 | anyhow = "1.0.70" 19 | clap = { version = "4.2.4", features = ["derive"] } 20 | -------------------------------------------------------------------------------- /thunk-cli/README.md: -------------------------------------------------------------------------------- 1 | # Thunk-cli 2 | 3 | Thunk the Rust program to support old Windows platforms! 4 | 5 | ## Preparation 6 | 7 | Download VC-LTL5 and YY-Thunks Binary, unzip them and add environment variable: 8 | 9 | | Binary | Environment Variable | 10 | | --- | ---| 11 | | VC-LTL-XXX-Binary.7z | VC_LTL | 12 | | YY-Thunks-XXX-Binary.zip | YY_THUNKS | 13 | 14 | Then add Thunk to run path. 15 | 16 | 17 | ## Install Thunk 18 | 19 | ``` 20 | cargo install thunk-cli 21 | ``` 22 | 23 | ## Sample 1. Build for Windows XP 24 | 25 | ``` 26 | cargo new build_for_xp 27 | cd build_for_xp 28 | thunk --os xp --arch x86 -- --release 29 | ``` 30 | 31 | ## Sample 2. Build a shared library for Windows XP 32 | 33 | ``` 34 | cargo new build_for_xp 35 | cd build_for_xp 36 | thunk --os xp --arch x86 --lib -- --release 37 | ``` 38 | 39 | ## Show help 40 | 41 | Use the following command to show help: 42 | 43 | ``` 44 | thunk.exe --help 45 | ``` 46 | 47 | Note: In order to distinguish the program build by Thunk, Thunk builds the release in `./target/*_build`. 48 | -------------------------------------------------------------------------------- /thunk-cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use clap::Parser; 3 | use std::{collections::HashMap, path::PathBuf, process::Command}; 4 | 5 | mod sys; 6 | use sys::*; 7 | 8 | const ENV_VAR_VC_LTL5: &str = "VC_LTL"; 9 | const ENV_VAR_YY_THUNKS: &str = "YY_THUNKS"; 10 | 11 | /// Use Thunk to build your Rust program that runs on old Windows platforms. 12 | #[derive(Debug, Parser)] 13 | pub struct ThunkBuilder { 14 | /// Operating system: xp, vista, win7, win10, 20h1 (dafault: win7) 15 | #[arg(short, long, value_name = "OS")] 16 | os: Option, 17 | /// Operating system arch: x86, x64, arm64 (dafault: current os arch) 18 | #[arg(short, long)] 19 | arch: Option, 20 | /// To build a shared library 21 | #[arg(long, value_name = "IS_LIB")] 22 | lib: bool, 23 | /// Link arg: console, windows (default: console) 24 | #[arg(short, long)] 25 | subsystem: Option, 26 | /// Args pass to cargo: cargo build 27 | #[arg(last = true, value_name = "CARGO_ARGS")] 28 | cargo_args: Vec, 29 | } 30 | 31 | impl ThunkBuilder { 32 | pub fn build(mut self) -> anyhow::Result { 33 | let env_vars: HashMap = std::env::vars().collect(); 34 | 35 | let mut vc_ltl = { 36 | let vc_ltl_env_path = env_vars.get(ENV_VAR_VC_LTL5).ok_or_else(|| { 37 | anyhow!("You need to set {} environment variable.", ENV_VAR_VC_LTL5) 38 | })?; 39 | PathBuf::from(vc_ltl_env_path) 40 | }; 41 | 42 | let os = self.os.unwrap_or(OS::Windows7); 43 | 44 | let arch = if let Ok(arch_from_args) = get_arch_from_args(self.cargo_args.as_slice()) { 45 | arch_from_args 46 | } else { 47 | let un_arch = self.arch.unwrap_or(get_default_arch()?); 48 | let target = un_arch 49 | .to_rust_target() 50 | .ok_or_else(|| anyhow!("arch {} fail translate to target", un_arch.to_string()))?; 51 | self.cargo_args.extend(["--target".to_owned(), target]); 52 | un_arch 53 | }; 54 | 55 | let os_lib = 56 | get_vc_ltl_os_lib_path(os, arch).ok_or_else(|| anyhow!("os or arch is wrong"))?; 57 | 58 | vc_ltl.push(os_lib); 59 | 60 | let os_version = 61 | get_os_version(os, arch).ok_or_else(|| anyhow!("failed to get os version"))?; 62 | 63 | let is_lib = { get_is_lib_from_args(self.cargo_args.as_slice()) || self.lib }; 64 | 65 | let mut subsystem = Some(self.subsystem.unwrap_or(Subsystem::Console)); 66 | 67 | if is_lib { 68 | subsystem = None; 69 | } 70 | 71 | let subsystem_args = 72 | subsystem.map(|x| format!("-Clink-args=/SUBSYSTEM:{},{}", x.to_string(), os_version)); 73 | 74 | let mut rust_flags = vec!["-L".into(), format!("{}", vc_ltl.to_string_lossy())]; 75 | 76 | if let Some(args) = subsystem_args { 77 | rust_flags.push(args.into()); 78 | 79 | if let Some(Subsystem::Windows) = subsystem { 80 | rust_flags.push("-Clink-args=/ENTRY:mainCRTStartup".into()) 81 | } 82 | } 83 | 84 | let thunks_obj = { 85 | let mut thunks = { 86 | let yy_thunks_env_path = env_vars.get(ENV_VAR_YY_THUNKS).ok_or_else(|| { 87 | anyhow!( 88 | "You need to set {} environment variable.", 89 | ENV_VAR_YY_THUNKS 90 | ) 91 | })?; 92 | PathBuf::from(yy_thunks_env_path) 93 | }; 94 | 95 | let os_obj = get_yy_thunks_obj_path(os, arch).ok_or_else(|| anyhow!(""))?; 96 | thunks.push(os_obj); 97 | Some(thunks) 98 | }; 99 | 100 | if let Some(obj) = thunks_obj { 101 | rust_flags.push(format!("-Clink-args={}", obj.to_string_lossy())); 102 | } 103 | 104 | let target_dir = format!("./target/win{}_build", os.to_string().to_ascii_lowercase()); 105 | 106 | let mut cargo_args = vec![ 107 | "build".to_owned(), 108 | "--target-dir".to_owned(), 109 | target_dir.clone(), 110 | ]; 111 | 112 | cargo_args.extend(self.cargo_args); 113 | 114 | let thunk = Thunk { 115 | rust_flags, 116 | cargo_args, 117 | os, 118 | arch, 119 | target_dir, 120 | }; 121 | 122 | Ok(thunk) 123 | } 124 | } 125 | 126 | #[derive(Debug)] 127 | pub struct Thunk { 128 | rust_flags: Vec, 129 | cargo_args: Vec, 130 | os: OS, 131 | arch: Arch, 132 | target_dir: String, 133 | } 134 | 135 | impl Thunk { 136 | pub fn run(self) { 137 | let rust_flags = self.rust_flags.join(" "); 138 | let cargo_args = self.cargo_args; 139 | 140 | println!( 141 | "Start to build for Windows {}({}) using VC-LTL and YY-Thunks: ", 142 | self.os.to_string(), 143 | self.arch.to_string(), 144 | ); 145 | println!(" * RUSTFLAGS = {}", rust_flags); 146 | println!(" * Command = cargo {}", cargo_args.join(" ")); 147 | println!("Cargo Output:"); 148 | 149 | let _status = Command::new("cargo") 150 | .env("RUSTFLAGS", rust_flags) 151 | .args(cargo_args) 152 | .status() 153 | .unwrap(); 154 | 155 | println!( 156 | "You can find the builds in target directory: {}", 157 | self.target_dir 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /thunk-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | fn main() { 4 | let thunk = thunk_cli::ThunkBuilder::parse().build().unwrap(); 5 | thunk.run(); 6 | } 7 | -------------------------------------------------------------------------------- /thunk-cli/src/sys.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::path::PathBuf; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub enum OS { 6 | WindowsXP, 7 | WindowsVista, 8 | Windows7, 9 | Windows8, 10 | Windows10, 11 | Windows10_20H1, 12 | } 13 | 14 | impl OS { 15 | pub fn from_str(text: &str) -> Self { 16 | match text.to_lowercase().as_str() { 17 | "windows xp" | "winxp" | "xp" | "5.1" | "5.2" | "2600" | "3790" | "2003" => { 18 | OS::WindowsXP 19 | } 20 | "windows vista" | "winvista" | "vista" | "6.0" | "6000" | "2008" => OS::WindowsVista, 21 | "windows 7" | "win7" | "7" | "6.1" | "7600" | "2008r2" => OS::Windows7, 22 | "windows 8" | "win8" | "8" | "6.2" | "9200" | "2012" => OS::Windows8, 23 | "windows 10" | "win10" | "10" | "10240" | "2016" | "2019" => OS::Windows10, 24 | "windows 10 20h1" | "win10 20h1" | "20h1" | "19041" | "2020" => OS::Windows10_20H1, 25 | _ => OS::WindowsXP, 26 | } 27 | } 28 | 29 | pub fn to_string(&self) -> String { 30 | match self { 31 | OS::WindowsXP => "XP".to_string(), 32 | OS::WindowsVista => "Vista".to_string(), 33 | OS::Windows7 => "7".to_string(), 34 | OS::Windows8 => "8".to_string(), 35 | OS::Windows10 => "10".to_string(), 36 | OS::Windows10_20H1 => "10_20h1".to_string(), 37 | } 38 | } 39 | } 40 | 41 | impl From for OS { 42 | fn from(value: String) -> Self { 43 | Self::from_str(&value) 44 | } 45 | } 46 | 47 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 48 | pub enum Arch { 49 | Win32, 50 | X64, 51 | ARM64, 52 | } 53 | 54 | impl Arch { 55 | pub fn from_str(text: &str) -> Self { 56 | match text.to_ascii_lowercase().as_str() { 57 | "win32" | "32" | "x86" | "86" | "i686" | "x32" => Arch::Win32, 58 | "64" | "x64" | "x86_64" | "x8664" | "amd64" => Arch::X64, 59 | "arm" | "aarch64" | "arm64" => Arch::ARM64, 60 | _ => Arch::Win32, 61 | } 62 | } 63 | 64 | pub fn from_rust_target(host: &str) -> Result { 65 | match host.to_ascii_lowercase().as_str() { 66 | "i686-pc-windows-msvc" => Ok(Arch::Win32), 67 | "x86_64-pc-windows-msvc" => Ok(Arch::X64), 68 | "aarch64-pc-windows-msvc" => Ok(Arch::ARM64), 69 | _ => Err(anyhow::anyhow!("Host {} is not support!", host)), 70 | } 71 | } 72 | 73 | pub fn to_string(&self) -> String { 74 | match self { 75 | Self::Win32 => "Win32".to_owned(), 76 | Self::X64 => "x64".to_owned(), 77 | Self::ARM64 => "ARM64".to_owned(), 78 | } 79 | } 80 | 81 | pub fn to_rust_target(&self) -> Option { 82 | match self { 83 | Arch::Win32 => Some("i686-pc-windows-msvc".to_owned()), 84 | Arch::X64 => Some("x86_64-pc-windows-msvc".to_owned()), 85 | Arch::ARM64 => Some("aarch64-pc-windows-msvc".to_owned()), 86 | } 87 | } 88 | } 89 | 90 | impl From for Arch { 91 | fn from(value: String) -> Self { 92 | Self::from_str(&value) 93 | } 94 | } 95 | 96 | pub fn get_vc_ltl_os_lib_path(os: OS, arch: Arch) -> Option { 97 | match (os, arch) { 98 | (OS::WindowsXP, Arch::Win32) => Some(PathBuf::from("TargetPlatform/5.1.2600.0/lib/Win32")), 99 | (OS::WindowsXP, Arch::X64) => Some(PathBuf::from("TargetPlatform/5.2.3790.0/lib/x64")), 100 | 101 | (OS::WindowsVista | OS::Windows7, Arch::Win32) => { 102 | Some(PathBuf::from("TargetPlatform/6.0.6000.0/lib/Win32")) 103 | } 104 | (OS::WindowsVista | OS::Windows7, Arch::X64) => { 105 | Some(PathBuf::from("TargetPlatform/6.0.6000.0/lib/x64")) 106 | } 107 | 108 | (OS::Windows8, Arch::Win32) => Some(PathBuf::from("TargetPlatform/6.2.9200.0/lib/Win32")), 109 | (OS::Windows8, Arch::X64) => Some(PathBuf::from("TargetPlatform/6.2.9200.0/lib/x64")), 110 | // (OS::Windows8, Arch::ARM) => Some(PathBuf::from("TargetPlatform/6.2.9200.0/lib/ARM")), 111 | (OS::Windows10, Arch::Win32) => { 112 | Some(PathBuf::from("TargetPlatform/10.0.10240.0/lib/Win32")) 113 | } 114 | (OS::Windows10, Arch::X64) => Some(PathBuf::from("TargetPlatform/10.0.10240.0/lib/x64")), 115 | (OS::Windows10, Arch::ARM64) => { 116 | Some(PathBuf::from("TargetPlatform/10.0.10240.0/lib/ARM64")) 117 | } 118 | 119 | (OS::Windows10_20H1, Arch::Win32) => { 120 | Some(PathBuf::from("TargetPlatform/10.0.19041.0/lib/Win32")) 121 | } 122 | (OS::Windows10_20H1, Arch::X64) => { 123 | Some(PathBuf::from("TargetPlatform/10.0.19041.0/lib/X64")) 124 | } 125 | (OS::Windows10_20H1, Arch::ARM64) => { 126 | Some(PathBuf::from("TargetPlatform/10.0.19041.0/lib/ARM64")) 127 | } 128 | _ => None, 129 | } 130 | } 131 | 132 | pub fn get_yy_thunks_obj_path(os: OS, arch: Arch) -> Option { 133 | match (os, arch) { 134 | (OS::WindowsXP, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_WinXP.obj")), 135 | (OS::WindowsXP, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_WinXP.obj")), 136 | (OS::WindowsVista, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_Vista.obj")), 137 | (OS::WindowsVista, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_Vista.obj")), 138 | (OS::Windows7, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_Win7.obj")), 139 | (OS::Windows7, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_Win7.obj")), 140 | (OS::Windows8, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_Win8.obj")), 141 | (OS::Windows8, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_Win8.obj")), 142 | (OS::Windows10, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_Win10.0.10240.obj")), 143 | (OS::Windows10, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_Win10.0.10240.obj")), 144 | (OS::Windows10_20H1, Arch::Win32) => Some(PathBuf::from("objs/x86/YY_Thunks_for_Win10.0.19041.obj")), 145 | (OS::Windows10_20H1, Arch::X64) => Some(PathBuf::from("objs/x64/YY_Thunks_for_Win10.0.19041.obj")), 146 | _ => None, 147 | } 148 | } 149 | 150 | pub fn get_os_version(os: OS, arch: Arch) -> Option { 151 | match (os, arch) { 152 | (OS::WindowsXP, Arch::Win32) => Some("5.01".to_owned()), 153 | (OS::WindowsXP, Arch::X64) => Some("5.02".to_owned()), 154 | (OS::WindowsVista, _) => Some("6.00".to_owned()), 155 | (OS::Windows7, _) => Some("6.01".to_owned()), 156 | (OS::Windows8, _) => Some("6.02".to_owned()), 157 | (OS::Windows10, _) => Some("10.0".to_owned()), 158 | (OS::Windows10_20H1, _) => Some("10.0".to_owned()), 159 | _ => None, 160 | } 161 | } 162 | 163 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 164 | pub enum Subsystem { 165 | Windows, 166 | Console, 167 | } 168 | 169 | impl Subsystem { 170 | pub fn from_str(subsystem: &str) -> Self { 171 | match subsystem.to_lowercase().as_ref() { 172 | "window" | "windows" | "win" | "w" | "gui" | "g" | "ui" | "u" => Self::Windows, 173 | "console" | "command" | "line" | "c" => Self::Console, 174 | _ => Self::Console, 175 | } 176 | } 177 | 178 | pub fn to_string(&self) -> String { 179 | match self { 180 | Subsystem::Windows => "WINDOWS".to_owned(), 181 | Subsystem::Console => "CONSOLE".to_owned(), 182 | } 183 | } 184 | } 185 | 186 | impl From for Subsystem { 187 | fn from(value: String) -> Self { 188 | Self::from_str(&value) 189 | } 190 | } 191 | 192 | pub fn get_default_arch() -> Result { 193 | get_default_arch_from_rustup() 194 | } 195 | 196 | fn get_default_arch_from_rustup() -> Result { 197 | let rustup_show = std::process::Command::new("rustup").arg("show").output()?; 198 | let output = String::from_utf8(rustup_show.stdout)?; 199 | let first: Vec<&str> = output 200 | .lines() 201 | .next() 202 | .ok_or_else(|| anyhow::anyhow!("Thunk is outdated"))? 203 | .split(':') 204 | .map(|x| x.trim()) 205 | .collect(); 206 | 207 | let host = first 208 | .get(1) 209 | .ok_or_else(|| anyhow::anyhow!("Thunk is outdated"))?; 210 | Arch::from_rust_target(host) 211 | } 212 | 213 | pub fn get_arch_from_args(args: I) -> Result 214 | where 215 | I: IntoIterator, 216 | S: AsRef, 217 | { 218 | let mut host: Option = None; 219 | let mut has_target = false; 220 | for arg in args { 221 | if arg.as_ref().starts_with("--target") { 222 | if arg.as_ref().contains("=") { 223 | let target: Vec<&str> = arg.as_ref().split('=').collect(); 224 | let target = target.get(1); 225 | host = target.map(|x| x.to_string()); 226 | break; 227 | } else { 228 | has_target = true; 229 | continue; 230 | }; 231 | } 232 | if has_target { 233 | host = Some(arg.as_ref().to_owned()); 234 | break; 235 | } 236 | } 237 | 238 | let host = host.ok_or_else(|| anyhow::anyhow!("Do not know arch"))?; 239 | Arch::from_rust_target(&host) 240 | } 241 | 242 | pub fn get_is_lib_from_args(args: I) -> bool 243 | where 244 | I: IntoIterator, 245 | S: AsRef, 246 | { 247 | for arg in args { 248 | if arg.as_ref() == "--lib" { 249 | return true; 250 | } 251 | } 252 | false 253 | } 254 | 255 | #[cfg(test)] 256 | mod tests { 257 | use super::*; 258 | 259 | #[test] 260 | fn test_get_arch_from_args() { 261 | let args = vec!["--target", "i686-pc-windows-msvc"]; 262 | let result = get_arch_from_args(&args).unwrap(); 263 | assert_eq!(Arch::Win32, result); 264 | 265 | let args = vec!["--target=i686-pc-windows-msvc"]; 266 | let result = get_arch_from_args(&args).unwrap(); 267 | assert_eq!(Arch::Win32, result); 268 | } 269 | 270 | #[test] 271 | fn test_get_arch_from_wrong_args() { 272 | let args: Vec<&str> = vec![]; 273 | assert_eq!(get_arch_from_args(&args).is_ok(), false); 274 | 275 | let args = vec!["target", "i686-pc-windows-msvc"]; 276 | assert_eq!(get_arch_from_args(&args).is_ok(), false); 277 | } 278 | 279 | #[test] 280 | fn test_get_is_lib_from_args() { 281 | let args: Vec<&str> = vec!["--lib"]; 282 | assert_eq!(get_is_lib_from_args(&args), true); 283 | 284 | let args = vec![""]; 285 | assert_eq!(get_is_lib_from_args(&args), false); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /thunk-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thunk-rs" 3 | version = "0.3.3" 4 | edition = "2021" 5 | authors = ["felixmaker"] 6 | description = "Thunk the Rust program to support Windows XP, Vista and more!" 7 | license = "Apache-2.0 OR MIT" 8 | keywords = ["build-dependencies"] 9 | readme = "README.md" 10 | categories = ["development-tools::build-utils"] 11 | repository = "https://github.com/felixmaker/thunk" 12 | documentation = "https://docs.rs/thunk-rs" 13 | 14 | [lib] 15 | name = "thunk" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | 20 | [features] 21 | default = ["xp"] 22 | xp = [] 23 | vista = [] 24 | win7 = [] 25 | win8 = [] 26 | win10_10240 = [] 27 | win10_19041 = [] 28 | vc_ltl_only = [] 29 | lib = [] 30 | subsystem_windows = [] 31 | windows_xp = ["xp"] 32 | windows_vista = ["vista"] 33 | win10 = ["win10_10240"] 34 | -------------------------------------------------------------------------------- /thunk-rs/README.md: -------------------------------------------------------------------------------- 1 | # thunk: Thunk the Rust program to support old Windows platforms! 2 | 3 | ## How does it work? 4 | 5 | Thunk uses [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) and [YY-Thunks](https://github.com/Chuyu-Team/YY-Thunks) to build program that supports even Windows XP. So, how does it work? 6 | 7 | - Add VC-LTL to the library search path 8 | - Use YY-Thunks to remedy API that old platform that does not exist 9 | 10 | Note: Thunk does not guarantee the compiled program work or work accurately on old platforms. 11 | **USE AT YOUR OWN RISK!** 12 | 13 | ## Usage 14 | 15 | Step1: Ensure command line tools `curl` and `7z` could be found in `PATH`. (Needed if `VC_LTL` and `YY_THUNKS` not found in environment variables) 16 | 17 | Step2: Add thunk as a build dependency: 18 | 19 | ``` 20 | cargo add thunk-rs --build 21 | ``` 22 | 23 | Step3: Create a build script build.rs: 24 | 25 | ``` 26 | fn main() { 27 | thunk::thunk(); 28 | } 29 | ``` 30 | 31 | Then, your program should run on Windows XP. 32 | 33 | ## Feature 34 | 35 | - xp: Enables VC-LTL5 and YY-Thunks to support Windows XP (default) 36 | - vista: Enables VC-LTL5 and YY-Thunks to support Windows Vista 37 | - win7: Enables VC-LTL5 and YY-Thunks to support Windows 7 38 | - win8: Enables VC-LTL5 and YY-Thunks to support Windows 8 39 | - win10: Enables VC-LTL5 and YY-Thunks to support Windows 10 40 | - vc_ltl_only: Enables VC-LTL5 to make the final executable run without VC runtime installed. 41 | - lib: Enables this when compiling a library. 42 | - subsystem_windows: Enables this when you want to hide console. 43 | 44 | ## Test Status 45 | 46 | - VC-LTL5: >= 5.1.1-Beta2 47 | - YY-Thunks: >= 1.1.1-Beta1 48 | -------------------------------------------------------------------------------- /thunk-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | use std::{env, path::PathBuf, process::Command}; 4 | 5 | const VC_LTL_VERSION: &'static str = "5.2.2-Beta1"; 6 | const YY_THUNKS_VERSION: &'static str = "1.1.7-Beta6"; 7 | 8 | /// This function should be call in build.rs. 9 | pub fn thunk() { 10 | let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); 11 | let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); 12 | 13 | if target_os != "windows" || target_env != "msvc" { 14 | println!("cargo::warning=Skipped! Only Windows(MSVC) is supported!"); 15 | return; 16 | } 17 | 18 | let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 19 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 20 | 21 | // Enable VC-LTL5 22 | let vc_ltl_arch = if target_arch == "x86" { "Win32" } else { "x64" }; 23 | let vc_ltl_platform = if cfg!(feature = "xp") { 24 | if vc_ltl_arch == "Win32" { 25 | "5.1.2600.0" 26 | } else { 27 | "5.2.3790.0" 28 | } 29 | } else if cfg!(feature = "vista") || cfg!(feature = "win7") { 30 | "6.0.6000.0" 31 | } else if cfg!(feature = "win8") { 32 | "6.2.9200.0" 33 | } else if cfg!(feature = "win10_10240") { 34 | "10.0.10240.0" 35 | } else if cfg!(feature = "win10_19041") { 36 | "10.0.19041.0" 37 | } else if cfg!(feature = "vc_ltl_only") { 38 | "6.0.6000.0" 39 | } else { 40 | println!("cargo::warning=VC-LTL5 Skipped: Nothing to do!"); 41 | return; 42 | }; 43 | 44 | let vc_ltl = get_or_download( 45 | "VC_LTL", 46 | "VC_LTL_URL", 47 | &format!( 48 | "https://github.com/Chuyu-Team/VC-LTL5/releases/download/v{}/VC-LTL-Binary.7z", 49 | VC_LTL_VERSION 50 | ), 51 | &out_dir, 52 | &format!("VC-LTL-{}", VC_LTL_VERSION), 53 | ); 54 | 55 | let vc_ltl_path = vc_ltl.join(&format!( 56 | "TargetPlatform/{}/lib/{}", 57 | vc_ltl_platform, vc_ltl_arch 58 | )); 59 | 60 | println!("cargo::rustc-link-search={}", vc_ltl_path.to_string_lossy()); 61 | println!( 62 | "cargo::warning=VC-LTL5 Enabled: {}({})", 63 | vc_ltl_platform, vc_ltl_arch 64 | ); 65 | 66 | // Enable YY-Thunks 67 | let yy_thunks_arch = if target_arch == "x86" { "x86" } else { "x64" }; 68 | let yy_thunks_platform = if cfg!(feature = "xp") { 69 | "WinXP" 70 | } else if cfg!(feature = "vista") { 71 | "Vista" 72 | } else if cfg!(feature = "win7") { 73 | "Win7" 74 | } else if cfg!(feature = "win8") { 75 | "Win8" 76 | } else if cfg!(feature = "win10_10240") { 77 | "Win10.0.10240" 78 | } else if cfg!(feature = "win10_19041") { 79 | "Win10.0.19041" 80 | } else { 81 | println!("cargo::warning=YY-Thunks Skipped: Nothing to do!!"); 82 | return; 83 | }; 84 | 85 | let yy_thunks = get_or_download( 86 | "YY_THUNKS", 87 | "YY_THUNKS_URL", 88 | &format!( 89 | "https://github.com/Chuyu-Team/YY-Thunks/releases/download/v{}/YY-Thunks-Objs.zip", 90 | YY_THUNKS_VERSION 91 | ), 92 | &out_dir, 93 | &format!("YY-Thunks-{}", YY_THUNKS_VERSION), 94 | ); 95 | 96 | let yy_thunks = yy_thunks.join(format!( 97 | "objs/{}/YY_Thunks_for_{}.obj", 98 | yy_thunks_arch, yy_thunks_platform 99 | )); 100 | 101 | println!("cargo::rustc-link-arg={}", yy_thunks.to_string_lossy()); 102 | println!( 103 | "cargo::warning=YY-Thunks Enabled: {}({})", 104 | yy_thunks_platform, yy_thunks_arch 105 | ); 106 | 107 | // Return if is lib mode 108 | if cfg!(feature = "lib") { 109 | println!("cargo::warning=Lib Mode Enabled!"); 110 | return; 111 | } 112 | 113 | // Set subsystem to windows 114 | let os_version = if cfg!(feature = "xp") { 115 | if target_arch == "x86" { 116 | ",5.01" 117 | } else { 118 | ",5.02" 119 | } 120 | } else { 121 | "" 122 | }; 123 | 124 | if cfg!(feature = "subsystem_windows") && env::var("PROFILE").unwrap() != "debug" { 125 | println!("cargo::rustc-link-arg=/SUBSYSTEM:WINDOWS{}", os_version); 126 | println!("cargo::rustc-link-arg=/ENTRY:mainCRTStartup"); 127 | println!("cargo::warning=Subsystem is set to WINDOWS"); 128 | } else { 129 | println!("cargo::rustc-link-arg=/SUBSYSTEM:CONSOLE{}", os_version); 130 | } 131 | } 132 | 133 | fn get_or_download( 134 | env_path: &str, 135 | env_url: &str, 136 | default_url: &str, 137 | out_dir: &PathBuf, 138 | unpack_name: &str, 139 | ) -> PathBuf { 140 | if let Ok(env_path) = env::var(env_path) { 141 | PathBuf::from(env_path) 142 | } else { 143 | let unpack_dir = out_dir.join(unpack_name); 144 | 145 | // Skip download if unpack dir exists. 146 | if unpack_dir.exists() { 147 | return unpack_dir; 148 | } 149 | 150 | let url = if let Ok(env_url) = env::var(env_url) { 151 | PathBuf::from(env_url) 152 | } else { 153 | PathBuf::from(default_url) 154 | }; 155 | 156 | let curl_status = Command::new("curl") 157 | .args(["-LOkf", url.to_str().unwrap()]) 158 | .current_dir(out_dir) 159 | .status() 160 | .expect("Curl is needed to download binaries!"); 161 | 162 | if !curl_status.success() { 163 | panic!("Download libraries from {:?} failed", url) 164 | } 165 | 166 | let extract_status = Command::new("7z") 167 | .args([ 168 | "x", 169 | "-aoa", 170 | url.file_name().unwrap().to_str().unwrap(), 171 | &format!("-o{}", unpack_name), 172 | ]) 173 | .current_dir(out_dir) 174 | .status() 175 | .expect("7z is needed to unpack libraries!"); 176 | 177 | if !extract_status.success() { 178 | panic!("Unpack YY-Thunks failed!") 179 | } 180 | 181 | unpack_dir 182 | } 183 | } 184 | --------------------------------------------------------------------------------