Compare commits

..
Author SHA1 Message Date
Hanif Koh 258bbee999 build_linux.sh: Forward Arguments After -- to the CMake Configure Step
Everything after `--` is appended, unchanged and last, to the cmake
configure line of the deps and OrcaSlicer builds, so a custom compiler,
compile flags or linker can be passed without adding an option to the
script for each one. The arguments are kept as an array, so a value with
spaces such as -DCMAKE_CXX_FLAGS="-O2 -march=native" survives, and they are
forwarded into the -g container. A stray argument without `--` aborts with
a message instead of reaching cmake as a path.

The existing DEPS_EXTRA_BUILD_ARGS and ORCA_EXTRA_BUILD_ARGS hooks are
unchanged and now mentioned in the usage text.
2026-10-14 14:45:30 +08:00
344 changed files with 8544 additions and 64772 deletions
-2
View File
@@ -8,7 +8,6 @@ on:
paths:
- 'deps/**'
- 'deps_src/**'
- 'cmake/**'
- 'src/**'
- '**/CMakeLists.txt'
- 'version.inc'
@@ -32,7 +31,6 @@ on:
paths:
- 'deps/**'
- 'deps_src/**'
- 'cmake/**'
- 'src/**'
- '**/CMakeLists.txt'
- 'version.inc'
+13 -32
View File
@@ -33,8 +33,6 @@ jobs:
ubuntu-ver: '2404'
ubuntu-ver-str: '_Ubuntu2404'
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
NSIS_VERSION: '3.13'
NSIS_SHA256: BA63DFFC4410EE89193E1CB5A41989991BD77C61068DA17E3156D136B7B0B3D8
steps:
- name: Checkout
@@ -429,46 +427,29 @@ jobs:
if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v3
# NSIS builds the installer. A pinned portable copy is kept in the Actions
# cache, so most runs never download it, and a cache miss fetches the
# release archive from SourceForge instead of going through the Chocolatey
# community feed, which hosted runners often cannot reach. Prepending it to
# PATH also makes cpack use it over the NSIS preinstalled on some images.
- name: Restore NSIS
id: nsis-cache
if: runner.os == 'Windows' && !vars.SELF_HOSTED
uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}/nsis
key: nsis-${{ env.NSIS_VERSION }}
- name: Install NSIS
- name: Install nsis
if: runner.os == 'Windows' && !vars.SELF_HOSTED
shell: pwsh
# The Chocolatey community feed intermittently 504s, and `choco install`
# exits 0 when package resolution fails that way. Unchecked, the job then
# builds for ~25 minutes before `cpack -G NSIS` reports a missing makensis.
# Retry the install and verify makensis itself, so a real failure stops here.
run: |
dir "C:/Program Files (x86)/Windows Kits/10/Include"
$nsisDir = Join-Path $env:RUNNER_TEMP 'nsis'
$nsisDir = Join-Path ${env:ProgramFiles(x86)} 'NSIS'
$makensis = Join-Path $nsisDir 'makensis.exe'
for ($attempt = 1; $attempt -le 3 -and -not (Test-Path $makensis); $attempt++) {
if ($attempt -gt 1) { Start-Sleep -Seconds (15 * $attempt) }
Write-Host "::group::choco install nsis (attempt $attempt)"
choco install nsis --yes --no-progress
Write-Host "::endgroup::"
}
if (-not (Test-Path $makensis)) {
$zip = Join-Path $env:RUNNER_TEMP "nsis-$env:NSIS_VERSION.zip"
curl.exe -fsSL --retry 5 --retry-all-errors --retry-delay 15 -o $zip "https://downloads.sourceforge.net/project/nsis/NSIS%203/$env:NSIS_VERSION/nsis-$env:NSIS_VERSION.zip"
if ($LASTEXITCODE -ne 0) { throw "NSIS $env:NSIS_VERSION download failed." }
if ((Get-FileHash $zip -Algorithm SHA256).Hash -ne $env:NSIS_SHA256) {
throw "NSIS $env:NSIS_VERSION archive does not match its pinned SHA256."
}
Expand-Archive $zip $env:RUNNER_TEMP
Move-Item (Join-Path $env:RUNNER_TEMP "nsis-$env:NSIS_VERSION") $nsisDir
throw "NSIS install failed: $makensis not found after 3 attempts."
}
& $makensis /VERSION
$nsisDir | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- name: Save NSIS
if: runner.os == 'Windows' && !vars.SELF_HOSTED && steps.nsis-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}/nsis
key: nsis-${{ env.NSIS_VERSION }}
- name: Build slicer Win
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}
+1 -2
View File
@@ -1345,8 +1345,7 @@ set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set (CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/images\\\\OrcaSlicer.ico")
set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}")
set (CPACK_NSIS_MUI_UNIICON "${CPACK_PACKAGE_ICON}")
# Relative to the install dir: CPack's NSIS template prefixes $INSTDIR itself.
set (CPACK_NSIS_INSTALLED_ICON_NAME "orca-slicer.exe")
set (CPACK_NSIS_INSTALLED_ICON_NAME "$INSTDIR\\\\orca-slicer.exe")
set (CPACK_PACKAGE_CHECKSUM SHA256)
set (CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OrcaSlicer")
set (CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
+1 -12
View File
@@ -86,8 +86,7 @@ The [wiki](https://www.orcaslicer.com/wiki) aims to provide a detailed explanati
## Stable Release
📥 **[Download the Latest Stable Release](https://github.com/OrcaSlicer/OrcaSlicer/releases/latest)**
Visit our GitHub Releases page for the latest stable version of OrcaSlicer, recommended for most users.
Windows release binaries are code signed; see our [Code signing policy](#code-signing-policy).
Visit our GitHub Releases page for the latest stable version of OrcaSlicer, recommended for most users.
## Nightly Builds
@@ -245,16 +244,6 @@ OrcaSlicer began in that same spirit, drawing from BambuStudio, PrusaSlicer, and
The OrcaSlicer logo was designed by community member [Justin Levine](https://github.com/jal-co).
# Code Signing Policy
Free code signing provided by [SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org).
Windows release binaries are built by GitHub Actions from this repository and signed through the SignPath Foundation program. Every signing request is manually approved.
**Team roles:** [SoftFever](https://github.com/SoftFever) is the committer, reviewer and approver for all signed releases.
**Privacy policy:** See the [OrcaSlicer privacy policy](https://www.orcaslicer.com/privacy-policy/).
# License
- **OrcaSlicer** is licensed under the GNU Affero General Public License, version 3.
-46
View File
@@ -42,7 +42,6 @@ call :add_arg build_deps bool d deps "Download and build the dependencies, neede
call :add_arg build_slicer bool s slicer "Build OrcaSlicer"
call :add_arg build_tests bool "" tests "Build the unit tests"
call :add_arg run_tests bool "" run-tests "Build the unit tests and run them"
call :add_arg make_installer bool "" installer "Build OrcaSlicer and package it into an NSIS installer"
call :add_arg pack_deps bool p pack "Bundle the built dependencies into a zip file"
call :add_arg install_deps bool u install-deps "Install or update CMake, Perl and Git with WinGet"
call :add_arg install_vs string "" install-vs "Also install Visual Studio: buildtools or ide"
@@ -186,9 +185,6 @@ if "%build_deps%%build_slicer%%pack_deps%%install_deps%%install_vs%" == "" (
if "%run_tests%" == "ON" set "build_slicer=ON"
)
REM The installer packages the slicer build, so it always needs one.
if "%make_installer%" == "ON" set "build_slicer=ON"
REM Options like --config or -j only shape a build. Without one of the actions
REM there is nothing for them to shape, so say so rather than resolving a whole
REM build and reporting it took no time. The block above may have added one.
@@ -226,30 +222,6 @@ if "%build_slicer%" == "ON" if "%install_slicer%" == "ON" if not "%slicer_target
echo --install needs the executable, but --slicer-target names "%slicer_target%".
exit /b 1
)
REM cpack runs the same install, so it has the same need.
if "%make_installer%" == "ON" if not "%slicer_target%" == "" if /I not "%slicer_target%" == "OrcaSlicer" (
echo --installer needs the executable, but --slicer-target names "%slicer_target%".
exit /b 1
)
REM cpack finds makensis on PATH or through the registry key the NSIS installer
REM writes, so look in the same places, and before the build rather than after
REM it. The value is read past its REG_SZ marker because the name column is
REM localised and may hold spaces.
if not "%make_installer%" == "ON" goto :nsis_ready
if "%dry_run%" == "ON" goto :nsis_ready
where makensis >nul 2>nul && goto :nsis_ready
for %%v in (32 64) do (
for /f "delims=" %%l in ('reg query "HKLM\SOFTWARE\NSIS" /ve /reg:%%v 2^>nul ^| findstr REG_SZ') do (
set "nsis_line=%%l"
set "nsis_line=!nsis_line:*REG_SZ=!"
for /f "tokens=*" %%d in ("!nsis_line!") do if exist "%%d\makensis.exe" goto :nsis_ready
)
)
echo NSIS was not found, and --installer needs it to build the installer.
echo Install it with "winget install NSIS.NSIS", then run this again.
exit /b 1
:nsis_ready
if not "%vs_version%" == "" if "!vs_gen_%vs_version%!" == "" (
echo Unknown Visual Studio release "%vs_version%". Known releases: 2019, 2022, 2026.
exit /b 1
@@ -762,14 +734,6 @@ if "%build_slicer%" == "ON" (
call :print_and_run cmake --build "%build_dir%" --target install --config %build_type%
%error_check%
)
REM cpack installs into its own staging tree, so this does not need -i.
REM Unlike CI it skips the preset cache, whose packaging step prunes the
REM checkout's preset JSONs.
if "%make_installer%" == "ON" (
call :print_and_run cpack -G NSIS -C %build_type% --config "%build_dir%\CPackConfig.cmake" -B "%build_dir%"
%error_check%
)
)
REM Elapsed wall clock. The 1%%a-100 trick strips a leading zero, which set /A
@@ -856,16 +820,6 @@ REM worked out the same way in either run.
if "%build_slicer%" == "ON" if not "%linked%" == "ON" echo Target %slicer_target%
if "%build_slicer%" == "ON" if not "%using_ninja%" == "ON" echo Solution %build_full%\!solution!
if "%pack_deps%" == "ON" if defined bundle echo Bundle !bundle!
if "%make_installer%" == "ON" (
REM The file name carries the version, which only CMake knows, so name
REM the newest match, or the pattern when there is none yet.
set "installer="
for /f "delims=" %%f in ('dir /b /o-d "!build_full!\OrcaSlicer_Windows_Installer_*.exe" 2^>nul') do (
if not defined installer set "installer=!build_full!\%%f"
)
if not defined installer set "installer=!build_full!\OrcaSlicer_Windows_Installer_*.exe"
echo Installer !installer!
)
echo.
echo Next
+109
View File
@@ -0,0 +1,109 @@
# Find the wxWidgets module based on the information provided by the Perl Alien::wxWidgets module.
# Check for the Perl & PerlLib modules
include(LibFindMacros)
libfind_package(AlienWx Perl)
libfind_package(AlienWx PerlLibs)
if (AlienWx_DEBUG)
message(STATUS " AlienWx_FIND_COMPONENTS=${AlienWx_FIND_COMPONENTS}")
endif()
# Execute an Alien::Wx module to find the relevant information regarding
# the wxWidgets used by the Perl interpreter.
# Perl specific stuff
set(AlienWx_TEMP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/AlienWx_TEMP_INCLUDE.txt)
execute_process(
COMMAND ${PERL_EXECUTABLE} -e "
# Import Perl modules.
use strict;
use warnings;
use Text::ParseWords;
BEGIN {
# CMake sets the environment variables CC and CXX to the detected C compiler.
# There is an issue with the Perl ExtUtils::CBuilder, which does not handle whitespaces
# in the paths correctly on Windows, so we rather drop the CMake auto-detected paths.
delete \$ENV{CC};
delete \$ENV{CXX};
}
use Alien::wxWidgets;
use ExtUtils::CppGuess;
# Test for a Visual Studio compiler
my \$cpp_guess = ExtUtils::CppGuess->new;
my \$mswin = \$^O eq 'MSWin32';
my \$msvc = \$cpp_guess->is_msvc;
# List of wxWidgets components to be used.
my @components = split /;/, '${AlienWx_FIND_COMPONENTS}';
# Query the available data from Alien::wxWidgets.
my \$version = Alien::wxWidgets->version;
my \$config = Alien::wxWidgets->config;
my \$compiler = Alien::wxWidgets->compiler;
my \$linker = Alien::wxWidgets->linker;
my \$include_path = ' ' . Alien::wxWidgets->include_path;
my \$defines = ' ' . Alien::wxWidgets->defines;
my \$cflags = Alien::wxWidgets->c_flags;
my \$linkflags = Alien::wxWidgets->link_flags;
my \$libraries = ' ' . Alien::wxWidgets->libraries(@components);
my \$gui_toolkit = Alien::wxWidgets->config->{toolkit};
#my @libraries = Alien::wxWidgets->link_libraries(@components);
#my @implib = Alien::wxWidgets->import_libraries(@components);
#my @shrlib = Alien::wxWidgets->shared_libraries(@components);
#my @keys = Alien::wxWidgets->library_keys; # 'gl', 'adv', ...
#my \$library_path = Alien::wxWidgets->shared_library_path;
#my \$key = Alien::wxWidgets->key;
#my \$prefix = Alien::wxWidgets->prefix;
my \$filename = '${AlienWx_TEMP_INCLUDE}';
open(my $fh, '>', \$filename) or die \"Could not open file '\$filename' \$!\";
# Convert a space separated lists to CMake semicolon separated lists,
# escape the backslashes,
# export the resulting list to a temp file.
sub cmake_set_var {
my (\$varname, \$content) = @_;
# Remove line separators.
\$content =~ s/\\r|\\n//g;
# Escape the path separators.
\$content =~ s/\\\\/\\\\\\\\\\\\\\\\/g;
my @words = shellwords(\$content);
print \$fh \"set(AlienWx_\$varname \\\"\" . join(';', @words) . \"\\\")\\n\";
}
cmake_set_var('VERSION', \$version);
\$include_path =~ s/ -I/ /g;
cmake_set_var('INCLUDE_DIRS', \$include_path);
\$libraries =~ s/ -L/ -LIBPATH:/g if \$msvc;
cmake_set_var('LIBRARIES', \$libraries);
#cmake_set_var('LIBRARY_DIRS', );
#\$defines =~ s/ -D/ /g;
cmake_set_var('DEFINITIONS', \$defines);
#cmake_set_var('DEFINITIONS_DEBUG', );
cmake_set_var('CXX_FLAGS', \$cflags);
cmake_set_var('GUI_TOOLKIT', \$gui_toolkit);
close \$fh;
")
include(${AlienWx_TEMP_INCLUDE})
file(REMOVE ${AlienWx_TEMP_INCLUDE})
unset(AlienWx_TEMP_INCLUDE)
if (AlienWx_DEBUG)
message(STATUS " AlienWx_VERSION = ${AlienWx_VERSION}")
message(STATUS " AlienWx_INCLUDE_DIRS = ${AlienWx_INCLUDE_DIRS}")
message(STATUS " AlienWx_LIBRARIES = ${AlienWx_LIBRARIES}")
message(STATUS " AlienWx_LIBRARY_DIRS = ${AlienWx_LIBRARY_DIRS}")
message(STATUS " AlienWx_DEFINITIONS = ${AlienWx_DEFINITIONS}")
message(STATUS " AlienWx_DEFINITIONS_DEBUG = ${AlienWx_DEFINITIONS_DEBUG}")
message(STATUS " AlienWx_CXX_FLAGS = ${AlienWx_CXX_FLAGS}")
message(STATUS " AlienWx_GUI_TOOLKIT = ${AlienWx_GUI_TOOLKIT}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(AlienWx
REQUIRED_VARS AlienWx_INCLUDE_DIRS AlienWx_LIBRARIES
# HANDLE_COMPONENTS
VERSION_VAR AlienWx_VERSION)
+88
View File
@@ -0,0 +1,88 @@
# Find the dependencies for linking with the Perl runtime library.
# Check for the Perl & PerlLib modules
include(LibFindMacros)
libfind_package(PerlEmbed Perl)
libfind_package(PerlEmbed PerlLibs)
# Execute an Alien::Wx module to find the relevant information regarding
# the wxWidgets used by the Perl interpreter.
# Perl specific stuff
set(PerlEmbed_TEMP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/PerlEmbed_TEMP_INCLUDE.txt)
execute_process(
COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e "
# Import Perl modules.
use strict;
use warnings;
use Config;
use Text::ParseWords;
use ExtUtils::CppGuess;
# Test for a Visual Studio compiler
my \$cpp_guess = ExtUtils::CppGuess->new;
my \$mswin = \$^O eq 'MSWin32';
my \$msvc = \$cpp_guess->is_msvc;
# Query the available data from Alien::wxWidgets.
my \$ccflags;
my \$ldflags;
{ local *STDOUT; open STDOUT, '>', \\\$ccflags; ccflags; }
{ local *STDOUT; open STDOUT, '>', \\\$ldflags; ldopts; }
\$ccflags = ' ' . \$ccflags;
\$ldflags = ' ' . \$ldflags;
my \$filename = '${PerlEmbed_TEMP_INCLUDE}';
open(my $fh, '>', \$filename) or die \"Could not open file '\$filename' \$!\";
# Convert a space separated lists to CMake semicolon separated lists,
# escape the backslashes,
# export the resulting list to a temp file.
sub cmake_set_var {
my (\$varname, \$content) = @_;
# Remove line separators.
\$content =~ s/\\r|\\n//g;
# Escape the path separators.
\$content =~ s/\\\\/\\\\\\\\\\\\\\\\/g;
my @words = shellwords(\$content);
print \$fh \"set(PerlEmbed_\$varname \\\"\" . join(';', @words) . \"\\\")\\n\";
}
cmake_set_var('ARCHNAME', \$Config{archname});
cmake_set_var('CCFLAGS', \$ccflags);
\$ldflags =~ s/ -L/ -LIBPATH:/g if \$msvc;
cmake_set_var('LD', \$Config{ld});
cmake_set_var('LDFLAGS', \$ldflags);
cmake_set_var('CCCDLFLAGS', \$Config{cccdlflags});
cmake_set_var('LDDLFLAGS', \$Config{lddlflags});
cmake_set_var('DLEXT', \$Config{dlext});
close \$fh;
")
include(${PerlEmbed_TEMP_INCLUDE})
file(REMOVE ${PerlEmbed_TEMP_INCLUDE})
unset(PerlEmbed_TEMP_INCLUDE)
if (PerlEmbed_DEBUG)
# First show the configuration extracted by FindPerl & FindPerlLibs:
message(STATUS " PERL_INCLUDE_PATH = ${PERL_INCLUDE_PATH}")
message(STATUS " PERL_LIBRARY = ${PERL_LIBRARY}")
message(STATUS " PERL_EXECUTABLE = ${PERL_EXECUTABLE}")
message(STATUS " PERL_SITESEARCH = ${PERL_SITESEARCH}")
message(STATUS " PERL_SITELIB = ${PERL_SITELIB}")
message(STATUS " PERL_VENDORARCH = ${PERL_VENDORARCH}")
message(STATUS " PERL_VENDORLIB = ${PERL_VENDORLIB}")
message(STATUS " PERL_ARCHLIB = ${PERL_ARCHLIB}")
message(STATUS " PERL_PRIVLIB = ${PERL_PRIVLIB}")
message(STATUS " PERL_EXTRA_C_FLAGS = ${PERL_EXTRA_C_FLAGS}")
# Second show the configuration extracted by this module (FindPerlEmbed):
message(STATUS " PerlEmbed_ARCHNAME = ${PerlEmbed_ARCHNAME}")
message(STATUS " PerlEmbed_CCFLAGS = ${PerlEmbed_CCFLAGS}")
message(STATUS " PerlEmbed_CCCDLFLAGS = ${PerlEmbed_CCCDLFLAGS}")
message(STATUS " LD = ${PerlEmbed_LD}")
message(STATUS " PerlEmbed_LDFLAGS = ${PerlEmbed_LDFLAGS}")
message(STATUS " PerlEmbed_LDDLFLAGS = ${PerlEmbed_LDDLFLAGS}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PerlEmbed
REQUIRED_VARS PerlEmbed_CCFLAGS PerlEmbed_LDFLAGS
VERSION_VAR PERL_VERSION)
+265
View File
@@ -0,0 +1,265 @@
# Version 2.2
# Public Domain, originally written by Lasse Kärkkäinen <tronic>
# Maintained at https://github.com/Tronic/cmake-modules
# Please send your improvements as pull requests on Github.
# Find another package and make it a dependency of the current package.
# This also automatically forwards the "REQUIRED" argument.
# Usage: libfind_package(<prefix> <another package> [extra args to find_package])
macro (libfind_package PREFIX PKG)
set(${PREFIX}_args ${PKG} ${ARGN})
if (${PREFIX}_FIND_REQUIRED)
set(${PREFIX}_args ${${PREFIX}_args} REQUIRED)
endif()
find_package(${${PREFIX}_args})
set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG})
unset(${PREFIX}_args)
endmacro()
# A simple wrapper to make pkg-config searches a bit easier.
# Works the same as CMake's internal pkg_check_modules but is always quiet.
macro (libfind_pkg_check_modules)
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(${ARGN} QUIET)
endif()
endmacro()
# Avoid useless copy&pasta by doing what most simple libraries do anyway:
# pkg-config, find headers, find library.
# Usage: libfind_pkg_detect(<prefix> <pkg-config args> FIND_PATH <name> [other args] FIND_LIBRARY <name> [other args])
# E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2)
function (libfind_pkg_detect PREFIX)
# Parse arguments
set(argname pkgargs)
foreach (i ${ARGN})
if ("${i}" STREQUAL "FIND_PATH")
set(argname pathargs)
elseif ("${i}" STREQUAL "FIND_LIBRARY")
set(argname libraryargs)
else()
set(${argname} ${${argname}} ${i})
endif()
endforeach()
if (NOT pkgargs)
message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.")
endif()
# Find library
libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs})
if (pathargs)
find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS})
endif()
if (libraryargs)
find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS})
endif()
endfunction()
# Extracts a version #define from a version.h file, output stored to <PREFIX>_VERSION.
# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR)
# Fourth argument "QUIET" may be used for silently testing different define names.
# This function does nothing if the version variable is already defined.
function (libfind_version_header PREFIX VERSION_H DEFINE_NAME)
# Skip processing if we already have a version or if the include dir was not found
if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR)
return()
endif()
set(quiet ${${PREFIX}_FIND_QUIETLY})
# Process optional arguments
foreach(arg ${ARGN})
if (arg STREQUAL "QUIET")
set(quiet TRUE)
else()
message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.")
endif()
endforeach()
# Read the header and parse for version number
set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
if (NOT EXISTS ${filename})
if (NOT quiet)
message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
endif()
return()
endif()
file(READ "${filename}" header)
string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}")
# No regex match?
if (match STREQUAL header)
if (NOT quiet)
message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"<version>\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
endif()
return()
endif()
# Export the version string
set(${PREFIX}_VERSION "${match}" PARENT_SCOPE)
endfunction()
# Do the final processing once the paths have been detected.
# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
# all the variables, each of which contain one include directory.
# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
# Also handles errors in case library detection was required, etc.
function (libfind_process PREFIX)
# Skip processing if already processed during this configuration run
if (${PREFIX}_FOUND)
return()
endif()
set(found TRUE) # Start with the assumption that the package was found
# Did we find any files? Did we miss includes? These are for formatting better error messages.
set(some_files FALSE)
set(missing_headers FALSE)
# Shorthands for some variables that we need often
set(quiet ${${PREFIX}_FIND_QUIETLY})
set(required ${${PREFIX}_FIND_REQUIRED})
set(exactver ${${PREFIX}_FIND_VERSION_EXACT})
set(findver "${${PREFIX}_FIND_VERSION}")
set(version "${${PREFIX}_VERSION}")
# Lists of config option names (all, includes, libs)
unset(configopts)
set(includeopts ${${PREFIX}_PROCESS_INCLUDES})
set(libraryopts ${${PREFIX}_PROCESS_LIBS})
# Process deps to add to
foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES})
if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS)
# The package seems to export option lists that we can use, woohoo!
list(APPEND includeopts ${${i}_INCLUDE_OPTS})
list(APPEND libraryopts ${${i}_LIBRARY_OPTS})
else()
# If plural forms don't exist or they equal singular forms
if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR
({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES))
# Singular forms can be used
if (DEFINED ${i}_INCLUDE_DIR)
list(APPEND includeopts ${i}_INCLUDE_DIR)
endif()
if (DEFINED ${i}_LIBRARY)
list(APPEND libraryopts ${i}_LIBRARY)
endif()
else()
# Oh no, we don't know the option names
message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!")
endif()
endif()
endforeach()
if (includeopts)
list(REMOVE_DUPLICATES includeopts)
endif()
if (libraryopts)
list(REMOVE_DUPLICATES libraryopts)
endif()
string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}")
if (NOT tmp STREQUAL "${includeopts} ${libraryopts}")
message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).")
endif()
# Include/library names separated by spaces (notice: not CMake lists)
unset(includes)
unset(libs)
# Process all includes and set found false if any are missing
foreach (i ${includeopts})
list(APPEND configopts ${i})
if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
list(APPEND includes "${${i}}")
else()
set(found FALSE)
set(missing_headers TRUE)
endif()
endforeach()
# Process all libraries and set found false if any are missing
foreach (i ${libraryopts})
list(APPEND configopts ${i})
if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
list(APPEND libs "${${i}}")
else()
set (found FALSE)
endif()
endforeach()
# Version checks
if (found AND findver)
if (NOT version)
message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.")
elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver))
set(found FALSE)
set(version_unsuitable TRUE)
endif()
endif()
# If all-OK, hide all config options, export variables, print status and exit
if (found)
foreach (i ${configopts})
mark_as_advanced(${i})
endforeach()
if (NOT quiet)
message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
if (LIBFIND_DEBUG)
message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}")
message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}")
message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}")
message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}")
message(STATUS " ${PREFIX}_LIBRARIES=${libs}")
endif()
set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE)
set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE)
set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE)
set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE)
set (${PREFIX}_FOUND TRUE PARENT_SCOPE)
endif()
return()
endif()
# Format messages for debug info and the type of error
set(vars "Relevant CMake configuration variables:\n")
foreach (i ${configopts})
mark_as_advanced(CLEAR ${i})
set(val ${${i}})
if ("${val}" STREQUAL "${i}-NOTFOUND")
set (val "<not found>")
elseif (val AND NOT EXISTS ${val})
set (val "${val} (does not exist)")
else()
set(some_files TRUE)
endif()
set(vars "${vars} ${i}=${val}\n")
endforeach()
set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n")
if (version_unsuitable)
set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but")
if (exactver)
set(msg "${msg} only version ${findver} is acceptable.")
else()
set(msg "${msg} version ${findver} is the minimum requirement.")
endif()
else()
if (missing_headers)
set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?")
elseif (some_files)
set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?")
if(findver)
set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).")
endif()
else()
set(msg "We were unable to find package ${PREFIX}.")
endif()
endif()
# Fatal error out if REQUIRED
if (required)
set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.")
message(FATAL_ERROR "${msg}\n${vars}")
endif()
# Otherwise just print a nasty warning
if (NOT quiet)
message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}")
endif()
endfunction()
+89
View File
@@ -0,0 +1,89 @@
# Source: https://gist.github.com/sivachandran/3a0de157dccef822a230#file-bin2h-cmake
# Added modifications to suit prusaslicer
include(CMakeParseArguments)
# Function to wrap a given string into multiple lines at the given column position.
# Parameters:
# VARIABLE - The name of the CMake variable holding the string.
# AT_COLUMN - The column position at which string will be wrapped.
function(WRAP_STRING)
set(oneValueArgs VARIABLE AT_COLUMN)
cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN})
string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength)
math(EXPR offset "0")
while(stringLength GREATER 0)
if(stringLength GREATER ${WRAP_STRING_AT_COLUMN})
math(EXPR length "${WRAP_STRING_AT_COLUMN}")
else()
math(EXPR length "${stringLength}")
endif()
string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line)
set(lines "${lines}\n${line}")
math(EXPR stringLength "${stringLength} - ${length}")
math(EXPR offset "${offset} + ${length}")
endwhile()
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction()
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file
# will contain a byte array and integer variable holding the size of the array.
# Parameters
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append
# to this name and will be used a variable name for size variable.
# HEADER_FILE - The path of header file.
# APPEND - If specified appends to the header file instead of overwriting it
# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be
# useful if the source file is a text file and we want to use the file contents
# as string. But the size variable holds size of the byte array without this
# null byte.
# Usage:
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG")
function(BIN2H)
set(options APPEND NULL_TERMINATE ADD_WARNING_TEXT)
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
# reads source file contents as hex string
file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
string(LENGTH ${hexString} hexStringLength)
# appends null byte if asked
if(BIN2H_NULL_TERMINATE)
set(hexString "${hexString}00")
endif()
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
wrap_string(VARIABLE hexString AT_COLUMN 32)
math(EXPR arraySize "${hexStringLength} / 2")
# adds '0x' prefix and comma suffix before and after every byte respectively
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
# removes trailing comma
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
# converts the variable name into proper C identifier
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# declares byte array and the length variables
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
set(warnTxt "")
if (BIN2H_ADD_WARNING_TEXT)
set(warnTxt "/* WARN: This file is auto-generated from ${BIN2H_SOURCE_FILE} */\n")
endif ()
set(declarations "${warnTxt}${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
if(BIN2H_APPEND)
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
else()
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
endif()
endfunction()
+71
View File
@@ -0,0 +1,71 @@
From b32e63f75046d186d93ee7d627c978f2f7c892f3 Mon Sep 17 00:00:00 2001
From: Steve Dower <steve.dower@python.org>
Date: Fri, 10 Jul 2026 14:04:00 +0100
Subject: [PATCH] [3.13] gh-153438: Update NuGet download URL (GH-153482)
(GH-153515)
gh-153438: Update NuGet download URL (GH-153482)
(cherry picked from commit 106eb532ea3b243423e62a702719e9d3c0e40c16)
(cherry picked from commit 62eb50d65d538dda5164f9c4a097a896454a0d1c)
Co-authored-by: Steve Dower <steve.dower@python.org>
Co-authored-by: Harjoth Khara <harjoth.khara@gmail.com>
---
Doc/using/windows.rst | 6 +++---
.../Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst | 2 ++
PCbuild/find_python.bat | 2 +-
Tools/msi/get_externals.bat | 2 +-
4 files changed, 7 insertions(+), 5 deletions(-)
create mode 100644 Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
index 2a507675044666..4800789ca2d6cc 100644
--- a/Doc/using/windows.rst
+++ b/Doc/using/windows.rst
@@ -414,9 +414,9 @@ on using nuget. What follows is a summary that is sufficient for Python
developers.
The ``nuget.exe`` command line tool may be downloaded directly from
-``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the
-tool, the latest version of Python for 64-bit or 32-bit machines is installed
-using::
+``https://dist.nuget.org/win-x86-commandline/latest/nuget.exe``, for example,
+using curl or PowerShell. With the tool, the latest version of Python for
+64-bit or 32-bit machines is installed using::
nuget.exe install python -ExcludeVersion -OutputDirectory .
nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory .
diff --git a/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst b/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
new file mode 100644
index 00000000000000..edab8e6ba7b259
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
@@ -0,0 +1,2 @@
+Update Windows build and installer tooling and documentation to use the
+current download URL for ``nuget.exe``.
diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat
index 0af367a3efafad..9612a2860ef9c3 100644
--- a/PCbuild/find_python.bat
+++ b/PCbuild/find_python.bat
@@ -52,7 +52,7 @@
@set _Py_HOST_PYTHON=%HOST_PYTHON%
@if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py
@if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe)
-@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl)
+@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
@if NOT exist "%_Py_NUGET%" (
@if not "%_Py_Quiet%"=="1" @echo Downloading nuget...
@rem NB: Must use single quotes around NUGET here, NOT double!
diff --git a/Tools/msi/get_externals.bat b/Tools/msi/get_externals.bat
index f6602ce9588ff4..c7c7e9f470bc25 100644
--- a/Tools/msi/get_externals.bat
+++ b/Tools/msi/get_externals.bat
@@ -6,7 +6,7 @@ set HERE=%~dp0
if "%PCBUILD%"=="" (set PCBUILD=%HERE%..\..\PCbuild\)
if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%HERE%..\..\externals\windows-installer)
if "%NUGET%"=="" (set NUGET=%EXTERNALS_DIR%\..\nuget.exe)
-if "%NUGET_URL%"=="" (set NUGET_URL=https://aka.ms/nugetclidl)
+if "%NUGET_URL%"=="" (set NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
set DO_FETCH=true
set DO_CLEAN=false
+15 -80
View File
@@ -8,45 +8,20 @@ set(_python_url "https://www.python.org/ftp/python/${_python_version}/Python-${_
set(_python_sha256 "c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684")
set(_patch_cmd "")
if(WIN32)
# Build-time Python for PCbuild's scripts, pinned because 3.15 cannot run the 3.12 scripts.
set(DEP_PYTHON_HOST "" CACHE FILEPATH
"Python 3.10-3.13 that runs CPython's build scripts; empty downloads a pinned one from nuget.org")
if(DEP_PYTHON_HOST)
execute_process(
COMMAND "${DEP_PYTHON_HOST}" -E -c "import sys; print('%d.%d' % sys.version_info[:2])"
OUTPUT_VARIABLE _python_host_minor
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _python_host_result
)
if(NOT _python_host_result EQUAL 0 OR _python_host_minor VERSION_LESS 3.10 OR _python_host_minor VERSION_GREATER 3.13)
message(FATAL_ERROR "DEP_PYTHON_HOST must be Python 3.10 to 3.13, got '${_python_host_minor}' from ${DEP_PYTHON_HOST}")
endif()
file(TO_NATIVE_PATH "${DEP_PYTHON_HOST}" _python_host)
else()
set(_python_host_version "3.13.16")
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
set(_python_host_package pythonarm64)
set(_python_host_sha256 bfd77aa5b0edff4a7936a3220e8b79e7c78f864ce13e8fa2c835d9946e11694a)
else()
set(_python_host_package pythonx86)
set(_python_host_sha256 c65cf47664b33286dfc5f2a17303668dd59e057869815ed9b270fb3866c1dd15)
endif()
set(_python_host_dir "${CMAKE_CURRENT_BINARY_DIR}/dep_python3_host-prefix/src/dep_python3_host")
ExternalProject_Add(dep_python3_host
URL "https://api.nuget.org/v3-flatcontainer/${_python_host_package}/${_python_host_version}/${_python_host_package}.${_python_host_version}.nupkg"
URL_HASH SHA256=${_python_host_sha256}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/python3
# Renames the .nupkg to .zip so ExternalProject extracts it.
DOWNLOAD_NAME ${_python_host_package}.${_python_host_version}.zip
SOURCE_DIR "${_python_host_dir}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
file(TO_NATIVE_PATH "${_python_host_dir}/tools/python.exe" _python_host)
set(_python_depends dep_python3_host)
endif()
# Fix python build failure on Windows if python is not available, due to wrong nuget download URL
# See https://github.com/python/cpython/issues/153438
# Patch from https://github.com/python/cpython/pull/153608
# This patch has not been merged to 3.12 yet so we need to apply it manually
#
# Without core.autocrlf=false the patched find_python.bat comes out LF and
# cmd.exe cannot find its goto labels.
set(_patch_cmd git init
&& ${GIT_EXECUTABLE} -c core.autocrlf=false apply --verbose
--ignore-space-change --whitespace=fix
${CMAKE_CURRENT_LIST_DIR}/01-windows-nuget.patch)
if(MSVC_VERSION EQUAL 1800)
set(_python_platform_toolset v120)
@@ -88,47 +63,6 @@ if(WIN32)
# release python3xx.lib; Py_DEBUG could not load release plugin modules.
set(_python_pcbuild_config Release)
# CPython's own libraries, fetched here instead of by get_externals.bat, which ignores failed
# downloads. Must match PCbuild/get_externals.bat for _python_version.
set(_python_externals
# repo folder commit sha256 of the archive
source-deps bzip2-1.0.8 05301997b2f9590f49c672cf3dfd3d3dfa7ad521 44b15bede52b32f8673a2976621a847258b041243c003e30f4d9a05214785fe5
source-deps sqlite-3.49.1.0 cb7a3997059739fe9c63b2819aeda7c6907510ef 586fe469463f5b27b749f9eef1938baa8e8a12030341e96160a371356c2d6759
source-deps xz-5.2.5 c6bc0c612605622aaef101a33a751f9de2ecc193 d99882a8f9fd48be0e605e27e3c077e5eaab370f6f52ebcc8ff754480ffdf0bc
source-deps zlib-1.3.1 4dc98e1909830e2bdc2a9cc2236e3c5d5037335b 39d867c083ebc37c77cf03a442f87c0f83db006eb64343bd4a19f1c84c26f50c
bin-deps libffi-3.4.4 94cb9a1c7feb608adf2b9f8fe2dbd6925ffbf90d 6b0f53490e0dd958708bff32df689cf2163c214147fcb8edb76a403fa6a877ca
bin-deps openssl-bin-3.0.16.2 1174fd43d858425c345b16d631716fb98ba492ea b741fd1411a36cb899fee2c39b70a4492f4bc1c6e2301d67df32e9754118a63b
)
set(_python_externals_dir "${CMAKE_CURRENT_BINARY_DIR}/dep_python3_externals")
list(LENGTH _python_externals _python_externals_len)
math(EXPR _python_externals_last "${_python_externals_len} - 1")
foreach(_i RANGE 0 ${_python_externals_last} 4)
math(EXPR _j "${_i} + 1")
math(EXPR _k "${_i} + 2")
math(EXPR _l "${_i} + 3")
list(GET _python_externals ${_i} _ext_repo)
list(GET _python_externals ${_j} _ext_name)
list(GET _python_externals ${_k} _ext_commit)
list(GET _python_externals ${_l} _ext_sha256)
ExternalProject_Add(dep_python3_ext_${_ext_name}
URL "https://github.com/python/cpython-${_ext_repo}/archive/${_ext_commit}.zip"
URL_HASH SHA256=${_ext_sha256}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/python3
DOWNLOAD_NAME cpython-${_ext_repo}-${_ext_name}.zip
SOURCE_DIR "${_python_externals_dir}/${_ext_name}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
list(APPEND _python_depends dep_python3_ext_${_ext_name})
endforeach()
file(TO_NATIVE_PATH "${_python_externals_dir}" _python_externals_native)
# PCbuild runs PYTHON without -E, so PYTHONHOME and PYTHONPATH are cleared.
set(_python_env_args --unset=PYTHONHOME --unset=PYTHONPATH "PYTHON=${_python_host}"
"EXTERNALS_DIR=${_python_externals_native}"
"GIT_CEILING_DIRECTORIES=<SOURCE_DIR>/..")
# CPython's PCbuild needs a 64-bit-hosted toolchain: find_msbuild.bat picks the
# 32-bit Bin\MSBuild.exe, whose x86 cl.exe/link.exe run out of address space
# (fatal C1002 "out of heap space") building the LTCG-optimized pythoncore.
@@ -137,6 +71,7 @@ if(WIN32)
# then hosts arm64 tools on its own; the amd64 MSBuild still defaults to x86, so
# PreferredToolArchitecture pins it to x64. Scoped to this build step, so
# CPython's own sources stay untouched.
set(_python_env_args "GIT_CEILING_DIRECTORIES=<SOURCE_DIR>/..")
if(CMAKE_GENERATOR_INSTANCE) # empty for non-VS generators (e.g. Ninja)
set(_python_msbuild "${CMAKE_GENERATOR_INSTANCE}/MSBuild/Current/Bin/${_python_msbuild_host}/MSBuild.exe")
if(EXISTS "${_python_msbuild}")
@@ -349,12 +284,12 @@ endif()
ExternalProject_Add(dep_python3
URL "${_python_url}"
URL_HASH SHA256=${_python_sha256}
PATCH_COMMAND ${_patch_cmd}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/python3
BUILD_IN_SOURCE ON
CONFIGURE_COMMAND ${_conf_cmd}
BUILD_COMMAND ${_build_cmd}
INSTALL_COMMAND ${_install_cmd}
DEPENDS ${_python_depends}
)
# Python depends on OpenSSL and ZLIB
-131
View File
@@ -1,131 +0,0 @@
# Color picker
The color picker is a local HTML dialog hosted by `ColorPickerDialog`, a
`WebViewHostDialog` subclass. Its header and implementation own the ordered
selection contract and shared filament conversion. Bridge validation, favorites
persistence, placement and gettext strings remain private to the implementation.
Its colors use the existing `ColorRGBA` utility.
It uses a native title bar and fixed window border, allowing dragging but no
manual resizing or maximization. Native title-bar colors use the application's
shared dialog dark-theme update on creation and theme changes.
Like SpeedDial, the host disables browser zoom accelerators, restores native
WebView focus on activation and readiness, and synchronizes layout and the macOS
viewport before repainting the backend. Activation never dismisses this modal
dialog; focus restoration preserves an active editor and its partial input.
The initialized page reports
its intrinsic content height; native code validates the current page identifier
and bounded numeric height, converts CSS pixels to device-independent window
size, and fits within the display's work area. Identical sizes are ignored. The
page observes content rather than viewport height, so resizing does not create
a feedback loop; oversized content remains scrollable.
The panel snapshots the triggering color or More Colors button's screen rectangle.
It aligns below the button, flips above when needed, and clamps to that button's
display work area on initial display. Later content and DPI changes update only
the client size, preserving the position chosen by dragging the title bar.
wxGTK clears the dialog's default
parent-centering constraint and reapplies its native position after showing,
bypassing wxGTK's requested-position cache. Absolute top-level placement remains
subject to the window manager; Wayland compositors control it. The official filament palette's
existing placement is independent of this custom panel.
A solid selection contains one color; a gradient contains exactly two ordered
endpoints. The bridge represents these as `{"type":"solid|gradient",
"colors":["#RRGGBBAA", ...]}`. RGB input is also accepted and becomes opaque.
Native validation rejects malformed hex and incorrect cardinality.
The project filament color action and the official filament picker's More Colors
action enable two-endpoint gradients and independent endpoint alpha. The preset
combo box reads project colors in their stored vector order locally and applies
changes through `sync_colour_config`,
retaining the existing RGB/RGBA profile/project formats and dirty marking.
Sidebar gradient and segmented multicolor swatches draw colors in the supplied
order without sorting them.
Opaque colors use `#RRGGBB`; nonopaque colors use `#RRGGBBAA`, including fully
transparent alpha `00`. Initial selections, alpha-only edits and cancellation
retain stored alpha. Ordered
custom results bypass the official palette's `FilamentColor` set. Choosing an
official swatch retains its existing palette behavior.
The Linux sidebar's Change extruder color menu uses this same custom editor and
the filament swatch anchor, including alpha, ordered gradients and configuration syncing.
Striped colors and gradients with more than two endpoints cannot be represented
by this editor. A notice explains that the current selection is retained; the
editor starts from the first color, and unchanged confirmation is a no-op.
Cancellation is also a no-op. If the WebView backend cannot initialize, a local
unavailable panel offers Cancel and Escape, without opening a system color picker.
Generic color option fields use an ordinary color button on every platform and
open the same panel with gradients and alpha disabled. The panel is anchored to
the field button. Empty or invalid field values remain undefined (the empty
string), distinct from opaque black. Right-click resets to undefined. Cancel does
not notify the field; confirmation and reset notify only when its RGB string
changes. Programmatic assignments remain silent, and reading a field never
writes legacy favorites.
AMS material settings retain their fixed/AMS swatches and first-level popup. Only
its custom-color action opens the solid, opaque editor, anchored to that custom
control. The transient popup releases its grab while the modal is open and returns
to the same selection on cancellation. Confirmation uses the existing default-color
and packed RGBA event path. Device permission/status checks remain at the first-level
entry.
Texture import's color-mapping Add Material action opens a solid, opaque editor
anchored to the mapping row. The existing filament limit is checked before opening.
The first-level material popup closes as an action; its callback is copied before
dismissal, so popup destruction does not invalidate the modal's completion path.
Confirmation invokes the existing virtual-filament callback once; cancellation
adds no material.
Native gettext strings are installed at document start. Static page labels and
palette names use stable English keys; RAL codes, channel symbols, and numeric
labels remain data. The palette color values do not depend on the UI language.
The application locale also sets the document language. Numeric editor validation
uses the application's gettext messages, rather than browser language defaults;
missing translations fall back to the stable English keys.
The pure color/state regressions run with
`node --test resources/web/dialog/ColorPickerDialog/colorpicker.test.js`.
Optional real-browser regressions live beside them in
`colorpicker.browser.test.js`. They use an existing Playwright installation
(`ORCA_PLAYWRIGHT_MODULE` can name its module path) and an installed browser
(`ORCA_BROWSER_EXECUTABLE` can name its executable), without adding a production
dependency. They cover bridge initialization, capability combinations, invalid
editor recovery, ordered endpoint alpha, favorites, gettext keys, and live theme
changes. These browser checks complement native contract/persistence tests; they
do not establish the full native modal confirmation and favorites journey.
Gradient and alpha capabilities are independent and default off. Unsupported
gradients are rejected; disabled alpha normalizes the selected result to opaque.
Favorites retain both capabilities independently of the current caller. The page
stores alpha as a byte and only converts to percentage when displaying or editing
opacity, so switching endpoints does not change alpha precision.
The page starts disabled. Each document supplies a page identifier in its ready
message, receives native initialization, and acknowledges it before native code
accepts confirmation or favorite updates. Repeated initialization does not reset
edits, and messages from an obsolete document are ignored. Native WebView message
handling is deferred and guarded by a lifetime token. Only a validated confirmation
sets the optional result. Cancel, Escape and native window close leave it empty.
Favorites belong to application configuration, separately from the selected result,
in the editor configuration returned by `AppConfig::config_path()` under `data_dir()`.
The `color_picker` section has string `version` equal to `1` and a `favorites`
string containing a JSON array of typed selections. Native helpers normalize and
deduplicate this array in order and limit it to 24 entries. Updates replace the
entire section and immediately call `AppConfig::save()`, so accepted favorite
changes survive cancellation. Empty arrays are saved explicitly. Core `AppConfig`
only stores the raw section; color validation belongs to the GUI boundary.
When the section is absent, valid solid colors from `custom_color_list` are imported
once, without changing that legacy section. The legacy native picker saves decimal
`r,g,b,a` byte strings; hexadecimal RGB and RGBA strings are also supported. Migration
strictly validates all four decimal channels and preserves alpha. Gradient strings
and malformed values are ignored. The new version and an empty array are
saved even when there is nothing to import. A present section with an unknown or
missing version is left untouched and disables favorite writes. Malformed version
1 data is displayed as empty and remains on disk until the user explicitly saves
a valid replacement. Other configuration sections and project/profile color
formats are independent of this storage.
Interface colors consume the host's `data-orca-theme` and `--orca-*` contract through
shared styles. The picker opts into scoped global control roles. Application theme
changes update UI surfaces without reloading the page or changing color data.
-66
View File
@@ -1,66 +0,0 @@
# Focus mode (hiding inactive plates) — High Level Design
## Purpose and scope
"Focus mode" is an entry of the canvas menu in the bottom left corner of Prepare. Turned on (eye
lit), the 3D view hides the inactive plates and shows the current plate alone, so a project with
many plates can be worked on one plate at a time. Turned off, every plate is shown. It is a view
setting: it changes nothing in the model, the slice or the project file, and it does not reach
plate thumbnails. The choice is kept in the app config as `focus_mode`, off by default.
Preview needs no such option, as it already draws the current plate alone and has its own plate
toolbar. The assembly view and the Design tab do not lay objects out on plates, so the entry is
disabled there.
## What is shown
The current plate is drawn with its bed, label and icons; the other plates are not drawn at all. The
world axes, which Prepare draws at the corner of the first plate, move to the corner of the current
plate, where Preview draws them.
An object instance is shown when its footprint, the XY bounding box of the convex hulls of its
volumes, overlaps the current plate, or when it is selected. The test is made per instance, so the
parts of an object are shown or hidden together, and a wipe tower follows its plate the same way.
It is geometric rather than the plate membership `PartPlateList` keeps, because an instance stays on
the plate it was first placed on: an object of another plate that reaches over this one, or one too
large for any plate, would otherwise be invisible while it takes room on the current plate. A
selected instance stays visible so that what a selection or a gizmo acts on is never hidden, for
instance an object picked in the object list on another plate.
## Hiding
A hidden object is neither drawn nor picked. `GLCanvas3D::_update_inactive_plates_visibility()`
runs at the start of each frame of Prepare. It clears `GLVolume::is_active` on the volumes of the
hidden instances and deactivates their raycasters, which every render pass, the hover and rectangle
selection, the shadow and center of mass markers already respect. It also deactivates the raycasters
of the other plates, whose icons would otherwise still take clicks where nothing is drawn. The state
persists between frames, so the raycasts made while handling the mouse, such as the anchor of a pan,
see the same scene as the frame.
Other features clear `is_active` too: gizmos hide the objects they do not edit and show them all
again when they close. `GLVolume::on_hidden_plate` records which volumes this update hid, so it only
shows again what it hid itself, and it waits for a gizmo that hides objects to close before doing
so. A volume another feature made active again is hidden again on the next frame. While Focus mode
is off and nothing is left hidden, the update returns at once.
## Switching plates
The other plates cannot be clicked while Focus mode hides them, so a compact counterpart of the Preview
plate toolbar takes their place: a column of plate thumbnails in the top left corner, below the top
toolbar, shown when the project has more than one plate and no gizmo is open. The thumbnails are the
ones the Preview toolbar shows (`PartPlate::thumbnail_data`); the canvas rebuilds its textures when
`Plater::plate_toolbar_image_version()` changes, a counter each consumer tracks on its own so that the
Preview toolbar's dirty flag is left alone. That counter moves after almost every edit, so the
thumbnail of the current plate, the only one edited while the others are hidden, is drawn again each
time, and the other plates' only once an edit to them has invalidated theirs. The current plate has an
accent border, a hovered one the gray border of the Preview toolbar, empty plates are dimmed and the
plate number sits in the corner; the tooltip gives the plate name. A click selects the plate and
clears the selection, as a click on a plate row of the object list does. The column scrolls when it
does not fit above the 3D navigator or the canvas toolbar. Its scrollbar works as the Preview
toolbar's, with which it shares its style: it shows only while the mouse is over the column or drags
the bar, and the mouse wheel scrolls the column either way.
Whatever selects a plate while the others are hidden, the camera moves its target over the new plate
and keeps its angle, distance and height (`PartPlateList::select_plate()`), so the plate comes into
view where the previous one was. A zoom to all plates, as after loading a project or adding a plate,
frames the current plate instead. Turning Focus mode on does the same, so the current plate fills the view.
+2 -3
View File
@@ -15,9 +15,8 @@ Outlines of centerlines that cross each other overlap at every crossing, which
over-extrudes the wall intersections. The line-crossing patterns Grid,
Triangles, Tri-hexagon and Cubic therefore build centerlines that never cross
(`FillRectilinear::fill_surface_trapezoidal()`), and so do Adaptive Cubic and
Support Cubic (`FillAdaptive`). 3D Honeycomb skips `multiline_fill()` and draws
its N lines itself as parallel copies of each wave, which never cross
(`Fill3DHoneycomb`). The other patterns outline their usual centerlines.
Support Cubic (`FillAdaptive`); the other patterns outline their usual
centerlines.
## Non-crossing centerlines
-131
View File
@@ -1,131 +0,0 @@
# Tooltip illustrations
The texture-displacement panel's tooltips can carry a picture under their text: a still image, or a
short animation that loops while the tooltip is open. Several settings in that panel describe a shape
rather than a number - what Rotation turns, what Tile repeats, what Invert turns inside out - and a
sentence about a shape is read twice and understood once.
## Where a tooltip is drawn
`GLGizmoTextureDisplacement::panel_tooltip()` replaces `ImGuiWrapper::tooltip()` throughout the panel.
It differs in two ways.
It places the tooltip beside the panel instead of under the mouse. The panel is pinned to the right
edge of the canvas, so a tooltip following the mouse covers the control it is explaining, and one
carrying a picture covers most of the panel with it. The tooltip goes to the left of
`m_tooltip_anchor_min`/`_max` - the panel's own screen rectangle, or the texture picker's while that
popup is up and the tooltip comes from inside it - and to the right only when there is no room on the
left. This is the placement the picker popup itself already uses.
ImGui normally keeps a tooltip glued to the mouse and ignores `SetNextWindowPos()`. Orca's imgui
branch honours a position set for a tooltip (see the `ImGuiWindowFlags_Tooltip` branch of `Begin()` in
`deps_src/imgui/imgui.cpp`) and still clamps the result inside the canvas, which is what keeps a tall
tooltip near the bottom edge on screen.
It also gives the tooltip a fixed width rather than one derived from its text, because a picture has
to be laid out against something, and a column of tooltips that are all one width is steadier to read.
## Where the pictures live
`resources/images/texture_displacement/`, as PNG, JPG, BMP or GIF. A tooltip names them by file:
```cpp
hover_tip(_u8L("Repeats the texture across the painted area. ..."),
{ { "tile_off.png", _L("Off") }, { "tile_on.png", _L("On") } });
```
Several in one tooltip are laid out side by side at equal widths, which is how a setting is shown as a
comparison - the same model at two values of it. The caption under each is translated; the file name
is not.
A file that is missing or will not decode is skipped, and a tooltip whose pictures are all missing is
exactly the text tooltip it was before. So the art for a tooltip can land after the tooltip does, and
a half-finished set never breaks the panel.
## When a tooltip appears
A tooltip waits for the pointer to rest on its control for 0.45 s. The panel is dense and the tooltip
lands on the model beside it, so with no wait a sweep from the top of the panel down to Bake flashes a
dozen large panels over the scene. Once one has just been up, the wait drops to 0.1 s for 0.7 s
afterwards, so reading several settings in a row stays quick - but not to zero, or that same sweep
would flash again the moment one tooltip had been seen.
No tooltip appears at all while a control is being used - a slider dragged, a value typed into one, a
combo open, a layer card being dragged. The pointer sits on the very thing being edited, so a tooltip
there covers the model exactly while the user is watching it change. The full wait comes back
afterwards rather than the short one: having just let go of a slider is not a reason to be shown a
picture of it.
A pointer resting on a control produces no events, and the canvas only renders when something asks it
to, so the wait has to ask for its own frames, and so does an animation once one is on screen. So does
a tooltip that has just appeared, animated or not: ImGui hides a window on the first frame it is
submitted while it measures it, and does so every time a tooltip reappears because those windows are
recycled, so the frame a tooltip is decided on is never the frame it is seen on. Both use
`GLCanvas3D::request_extra_frame()`, which ends `on_idle()` with `evt.RequestMore()` and so obliges wx
to deliver the next idle. `schedule_extra_frame()` is not enough on its own: it only arranges a timer
whose event an idle event is assumed to follow, and on_idle renders nothing unless the canvas was
already dirty when it ran. Asking for frames as fast as they come is safe here because which frame is
drawn comes from the clock, not from the frame count, so a redraw that arrives early simply draws the
same frame again.
## Decoding and playback
`slic3r/GUI/AnimatedTexture.hpp` decodes a file once and uploads one GL texture per frame. Stills go
through `wxImage`; GIFs go through `wxGIFDecoder`, whose raw palette indices are composited here frame
by frame, replaying the disposal method each frame asks for. That replay is what makes an animation
look continuous: a GIF frame is a sub-rectangle drawn over what came before it, so an animation that
only re-sends the pixels that changed draws as a flicker of disconnected patches without it.
A frame delay of 0 or 1 hundredths of a second is read as 100 ms, the convention browsers settle on,
so an animation authored against that plays at the speed it was authored at.
The animation restarts when its tooltip opens, rather than running off a free-running clock: these are
short loops of one setting going from one value to another, and joining one halfway through shows the
end of a change the user never saw the start of.
Which frame is drawn is measured on a real clock, not `ImGui::GetTime()`. Nothing in OrcaSlicer ever sets
`io.DeltaTime`, so it keeps imgui's default of 1/60 per frame and imgui's clock counts frames rather
than seconds. On a canvas that renders only when asked, that is not a clock at all: an animation driven
by it crawls while the canvas is idle and races while a moving mouse is rendering it flat out.
## Making one
`scripts/gen_texture_displacement_tips.py` draws the lot, in four languages, picked by what the
setting actually is:
- **A shaded relief**, for the settings that are about a pattern: Depth, Tile, Tile size, Rotation,
Repeat/Mirror, Smoothing, Edge fade, Invert, Blend and the Normal, Fast, Checker and Distortion
views. One motif - a ring inside a square frame - runs through all of them, because using one shape
everywhere is what lets two pictures be compared: whatever differs between them is the setting,
since nothing else is.
- **A flat mesh of triangles**, for the settings that are about the mesh or about a tool: the paint
tools, brush size, the connected-area fill, subdivision, feature-adaptive refinement, remeshing,
hard edges, slivers and the wireframe view. These carry the one accent colour in the set, meaning
"painted", because that is the only thing in such a picture that is not geometry.
- **A section**, for Midlevel and for smoothing the geometry. The reason generalises: shading reads
slopes, so a height field moved up or down as a whole shades identically to where it started, and
any setting whose meaning is *where the surface sits relative to where it was* is invisible from
above and has to be drawn from the side.
- **A flat ramp of greys**, for Mix filaments, Mixed colors and Denoise. Those are about which
filament a spot ends up as; lighting one would only add a second thing changing between two
pictures meant to differ in one.
The five Mapping pictures are one ball with a checker laid on it five ways, which is the whole of what
choosing a projection does - except Unwrap, which is not a projection onto the model at all and so is
drawn as the flat layout it produces.
Captures of the real tool are still the first choice for anything involving a real model or real
filament colour: Bake, the mixed-colour count on a photograph, Adjust placement, the frame window and
the UV editor's seam tool have no picture yet for that reason.
Either way:
- Square, around 256 px (192 for an animation, where every frame ships). The tooltip is about 20 em
wide, so that is roughly native size on a 100% display and downscales cleanly above it. A single
picture is capped at 10 em tall and centred, two side by side take half the width each.
- Neutral grey. These say what a shape does; colour in one reads as a third thing the setting does,
and has to work against both themes.
- Animations loop seamlessly and stay short - 16 frames at 100 ms, under about 150 KB. They sit in
`resources/`, which ships with every copy of the application. A sweep driven by a cosine over the
frame count loops with no step, since it starts and ends at the same value and the same rate.
- No text in the picture. Captions are translated; pixels are not.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-3
View File
@@ -82,7 +82,6 @@ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp
src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp
src/slic3r/GUI/Gizmos/GLGizmoAlignment.cpp
src/slic3r/GUI/GUI.cpp
src/slic3r/GUI/GUI_App.cpp
src/slic3r/GUI/GUI_AuxiliaryList.cpp
@@ -215,7 +214,6 @@ src/libslic3r/GCode.cpp
src/libslic3r/GCodeWriter.cpp
src/libslic3r/GCode/ToolOrdering.cpp
src/libslic3r/GCode/SeamPlacer.cpp
src/libslic3r/GCode/GCodeStatistics.cpp
src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp
src/libslic3r/Format/AMF.cpp
@@ -325,7 +323,6 @@ src/slic3r/GUI/BaseTransparentDPIFrame.cpp
src/slic3r/GUI/CloneDialog.cpp
src/slic3r/GUI/ColorDecomposeDialog.cpp
src/slic3r/GUI/ColorDecomposeSupport.cpp
src/slic3r/GUI/ColorPickerDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DeviceCore/DevCalib.cpp
src/slic3r/GUI/DeviceCore/DevUpgrade.cpp
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1="7.5" y1=".5" x2="7.5" y2="2.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="7.5" y1="6.5" x2="7.5" y2="8.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="7.5" y1="12.5" x2="7.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="5.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="1.5" y="8.5" width="12" height="4" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 684 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1="7.5" y1=".5" x2="7.5" y2="2.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="7.5" y1="6.5" x2="7.5" y2="8.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="7.5" y1="12.5" x2="7.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="5.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="1.5" y="8.5" width="12" height="4" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 684 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y="8.5" width="10" height="4" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/><line x1="14.5" y1=".5" x2="14.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="8.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 439 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y="8.5" width="10" height="4" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/><line x1="14.5" y1=".5" x2="14.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="8.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 439 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y="8.5" width="10" height="4" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/><line x1=".5" y1=".5" x2=".5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 435 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y="8.5" width="10" height="4" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/><line x1=".5" y1=".5" x2=".5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="2.5" width="4" height="4" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 435 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1="7.5" x2="2.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="6.5" y1="7.5" x2="8.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="12.5" y1="7.5" x2="14.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="5.5" width="4" height="4" transform="translate(-3 12) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 765 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1="7.5" x2="2.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="6.5" y1="7.5" x2="8.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><line x1="12.5" y1="7.5" x2="14.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="5.5" width="4" height="4" transform="translate(-3 12) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 765 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1=".5" x2="14.5" y2=".5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="2.5" width="4" height="4" transform="translate(0 9) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 514 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1=".5" x2="14.5" y2=".5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="2.5" width="4" height="4" transform="translate(0 9) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 514 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1="14.5" x2="14.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="8.5" width="4" height="4" transform="translate(-6 15) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#7c8282; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 520 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><line x1=".5" y1="14.5" x2="14.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><rect x="2.5" y="8.5" width="4" height="4" transform="translate(-6 15) rotate(-90)" style="fill:none; stroke:#009688; stroke-miterlimit:10;"/><rect x="5.5" y="5.5" width="10" height="4" transform="translate(3 18) rotate(-90)" style="fill:none; stroke:#949494; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 520 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 7.5 7.5 10.5 .5 7.5 7.5 4.5 14.5 7.5" style="fill:none; stroke:#7c8282; stroke-linejoin:round;"/><polyline points="9.5 .5 7.5 2.5 5.5 .5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="5.5 14.5 7.5 12.5 9.5 14.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 479 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 7.5 7.5 10.5 .5 7.5 7.5 4.5 14.5 7.5" style="fill:none; stroke:#949494; stroke-linejoin:round;"/><polyline points="9.5 .5 7.5 2.5 5.5 .5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="5.5 14.5 7.5 12.5 9.5 14.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 479 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 3.5 7.5 6.5 .5 3.5 7.5 .5 14.5 3.5" style="fill:none; stroke:#7c8282; stroke-linejoin:round;"/><line x1="7.5" y1="8.5" x2="7.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><polyline points="5.5 10.5 7.5 8.5 9.5 10.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 478 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 3.5 7.5 6.5 .5 3.5 7.5 .5 14.5 3.5" style="fill:none; stroke:#949494; stroke-linejoin:round;"/><line x1="7.5" y1="8.5" x2="7.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><polyline points="5.5 10.5 7.5 8.5 9.5 10.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 478 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 11.5 7.5 14.5 .5 11.5 7.5 8.5 14.5 11.5" style="fill:none; stroke:#7c8282; stroke-linejoin:round;"/><line x1="7.5" y1=".5" x2="7.5" y2="6.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><polyline points="9.5 4.5 7.5 6.5 5.5 4.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 479 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 11.5 7.5 14.5 .5 11.5 7.5 8.5 14.5 11.5" style="fill:none; stroke:#949494; stroke-linejoin:round;"/><line x1="7.5" y1=".5" x2="7.5" y2="6.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-miterlimit:10;"/><polyline points="9.5 4.5 7.5 6.5 5.5 4.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 479 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="-2.5" y="5.5" width="10" height="4" transform="translate(-5 10) rotate(-90)" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><rect x="7.5" y="5.5" width="10" height="4" transform="translate(5 20) rotate(-90)" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><line x1="7.5" y1="5.5" x2="7.5" y2="9.5" style="fill:none; stroke:#7c8282; stroke-linecap:square; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 522 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="-2.5" y="5.5" width="10" height="4" transform="translate(-5 10) rotate(-90)" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><rect x="7.5" y="5.5" width="10" height="4" transform="translate(5 20) rotate(-90)" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><line x1="7.5" y1="5.5" x2="7.5" y2="9.5" style="fill:none; stroke:#949494; stroke-linecap:square; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 522 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y=".5" width="10" height="4" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><rect x="2.5" y="10.5" width="10" height="4" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><line x1="9.5" y1="7.5" x2="5.5" y2="7.5" style="fill:none; stroke:#7c8282; stroke-linecap:square; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 440 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.5" y=".5" width="10" height="4" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><rect x="2.5" y="10.5" width="10" height="4" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><line x1="9.5" y1="7.5" x2="5.5" y2="7.5" style="fill:none; stroke:#949494; stroke-linecap:square; stroke-miterlimit:10;"/></svg>

Before

Width:  |  Height:  |  Size: 440 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 3.5 7.5 6.5 .5 3.5 7.5 .5 14.5 3.5" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><polyline points="2.83 10.5 .5 11.5 7.5 14.5 14.5 11.5 12.17 10.5" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><polyline points="2.83 6.5 .5 7.5 7.5 10.5 14.5 7.5 12.17 6.5" style="fill:none; stroke:#7c8282; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 476 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon points="14.5 3.5 7.5 6.5 .5 3.5 7.5 .5 14.5 3.5" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><polyline points="2.83 10.5 .5 11.5 7.5 14.5 14.5 11.5 12.17 10.5" style="fill:none; stroke:#009688; stroke-linejoin:round;"/><polyline points="2.83 6.5 .5 7.5 7.5 10.5 14.5 7.5 12.17 6.5" style="fill:none; stroke:#949494; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Some files were not shown because too many files have changed in this diff Show More