Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,9 @@ if (WIN32)

include(CPack)
endif()

if (WIN32)
# Portable (no-install) package target. Must come last so its dependency
# collection sees every target defined above.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/portable.cmake)
endif()
62 changes: 62 additions & 0 deletions cmake/portable.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Windows portable (no-install) package.
#
# `cmake --build <build> --target portable` produces:
# <build>/portable/xSTUDIO-<version>-win64/ staged, relocatable folder
# <build>/xSTUDIO-<version>-win64-portable.zip archive with one top-level folder
#
# Included from the top-level CMakeLists.txt (Windows only), after every
# add_subdirectory() call so the target collection below sees all targets.
# The staging/prune/zip work lives in scripts/portable/portable.cmake.in,
# configured into the build tree as portable.cmake.

set(XSTUDIO_PORTABLE_NAME "xSTUDIO-${XSTUDIO_GLOBAL_VERSION}-win64")
set(XSTUDIO_PORTABLE_STAGE "${CMAKE_BINARY_DIR}/portable/${XSTUDIO_PORTABLE_NAME}")

configure_file(scripts/portable/portable.cmake.in
${CMAKE_BINARY_DIR}/portable.cmake @ONLY)

add_custom_target(portable
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XSTUDIO_PORTABLE_STAGE}"
COMMAND ${CMAKE_COMMAND} --install "${CMAKE_BINARY_DIR}"
--prefix "${XSTUDIO_PORTABLE_STAGE}" --config $<CONFIG>
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_BINARY_DIR}/portable.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Building portable xSTUDIO package"
USES_TERMINAL VERBATIM)

# Make `portable` depend on every buildsystem target so it builds the same
# set as `--target package`. ALL membership is not enough: targets such as
# python_module and *_COPY_QML are add_custom_target(... ALL ...) and only
# run when explicitly requested or depended upon. Targets without ALL
# membership (Qt qmllint/qmlcachegen helpers, clang-tidy, clangformat,
# `portable` itself) report EXCLUDE_FROM_ALL and are skipped; the denylist is
# a belt-and-braces guard for the always-defined lint targets.
set(XSTUDIO_PORTABLE_DENYLIST portable clang-tidy clangformat)

function(_xstudio_portable_collect_targets dir)
get_directory_property(_targets DIRECTORY "${dir}" BUILDSYSTEM_TARGETS)
foreach(_target IN LISTS _targets)
get_target_property(_excluded "${_target}" EXCLUDE_FROM_ALL)
if(_excluded)
continue()
endif()
get_target_property(_type "${_target}" TYPE)
if(_type STREQUAL "INTERFACE_LIBRARY")
continue()
endif()
list(FIND XSTUDIO_PORTABLE_DENYLIST "${_target}" _denied)
if(_denied GREATER_EQUAL 0)
continue()
endif()
set_property(GLOBAL APPEND PROPERTY _XSTUDIO_PORTABLE_TARGETS "${_target}")
endforeach()

get_directory_property(_subdirs DIRECTORY "${dir}" SUBDIRECTORIES)
foreach(_subdir IN LISTS _subdirs)
_xstudio_portable_collect_targets("${_subdir}")
endforeach()
endfunction()

_xstudio_portable_collect_targets("${CMAKE_SOURCE_DIR}")
get_property(_xstudio_portable_targets GLOBAL PROPERTY _XSTUDIO_PORTABLE_TARGETS)
add_dependencies(portable ${_xstudio_portable_targets})
17 changes: 17 additions & 0 deletions docs/reference/build_guides/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ RelWithDebInfo and Debug variants are also available — see [CMakePresets.json]

If the build is successful, you should have an executable in the 'build' folder called something like 'xSTUDIO-1.2.0-win64.exe'. This can be executed to start the xSTUDIO installer.

### Portable build (no installer)

As an alternative to the NSIS installer you can build a relocatable, no-install folder plus a zip archive:

cmake --build build --target portable

This produces:

- `build/portable/xSTUDIO-<version>-win64/` - the staged package (kept for inspection), run it via `xstudio.bat` or `bin\xstudio.exe`
- `build/xSTUDIO-<version>-win64-portable.zip` - the same folder as a single archive

Notes:

- Like `--target package`, the `portable` target re-runs the full install including `windeployqt`, so it is about as slow.
- The folder is relocatable, but not data-isolated: preferences, autosaves and thumbnails are still written under the Windows user profile (see the package's `README.txt` for the exact paths).
- `.xst` file associations and Start-menu entries are installer-only and are not part of the portable package.

### Running xSTUDIO from the build tree (dev workflow)

For a quick dev run without going through the installer, the build generates a launcher at `build/run_xstudio.bat`. Arguments are forwarded to xstudio:
Expand Down
19 changes: 19 additions & 0 deletions scripts/portable/README.txt.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
xSTUDIO @XSTUDIO_GLOBAL_VERSION@ - portable build (Windows x64)
===============================================================

No installation required: run xstudio.bat (or bin\xstudio.exe) directly.
The folder is fully relocatable - move or unpack it anywhere and run it.

Note that this build is relocatable but not data-isolated. Preferences,
autosaves and thumbnail caches are still written under the Windows user
profile, shared with any other xSTUDIO installation:

%USERPROFILE%\.config\DNEG\xstudio\preferences
%USERPROFILE%\xStudio\autosave
%USERPROFILE%\xStudio\thumbnails

.xst session-file associations and Start-menu entries are created by the
NSIS installer only; they are not part of this portable package.

Documentation is available from the Help menu inside xSTUDIO
(share/xstudio/docs).
94 changes: 94 additions & 0 deletions scripts/portable/portable.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Generated from scripts/portable/portable.cmake.in by cmake/portable.cmake.
# Runs as `cmake -P` after the full install has been staged at
# @XSTUDIO_PORTABLE_STAGE@. Sanity-checks, prunes dev-only content, adds the
# launcher/README and zips the result.

set(XSTUDIO_PORTABLE_STAGE "@XSTUDIO_PORTABLE_STAGE@")
set(XSTUDIO_PORTABLE_NAME "@XSTUDIO_PORTABLE_NAME@")
set(XSTUDIO_GLOBAL_VERSION "@XSTUDIO_GLOBAL_VERSION@")
set(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@")
set(CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@")

# 1. Sanity check the staged install before touching anything.
set(_required_paths
"${XSTUDIO_PORTABLE_STAGE}/bin/xstudio.exe"
"${XSTUDIO_PORTABLE_STAGE}/bin/python3/python.exe"
"${XSTUDIO_PORTABLE_STAGE}/bin/Qt6Core.dll"
"${XSTUDIO_PORTABLE_STAGE}/bin/qml"
"${XSTUDIO_PORTABLE_STAGE}/share/xstudio/plugin")
foreach(_path IN LISTS _required_paths)
if(NOT EXISTS "${_path}")
message(FATAL_ERROR
"Portable packaging failed: '${_path}' is missing from the staged "
"install at '${XSTUDIO_PORTABLE_STAGE}'. Build the project fully "
"before running the 'portable' target.")
endif()
endforeach()

# 2. Guard: on Windows share/xstudio/lib should only receive import libs
# (cmake/macros.cmake default_options -> LIBRARY DESTINATION share/xstudio/lib).
# Refuse to prune it if it ever contains runtime DLLs.
file(GLOB _lib_dlls "${XSTUDIO_PORTABLE_STAGE}/share/xstudio/lib/*.dll")
if(_lib_dlls)
message(FATAL_ERROR
"Portable packaging aborted: '${XSTUDIO_PORTABLE_STAGE}/share/xstudio/lib' "
"contains runtime DLLs: ${_lib_dlls}. The portable step prunes that "
"directory because on Windows it is expected to hold only import libs. "
"Update scripts/portable/portable.cmake.in if this layout change is intended.")
endif()

# Same guard for lib/: on Windows it receives only CMake package config and
# import libs (ARCHIVE default destination of install(TARGETS ... EXPORT)).
file(GLOB _root_lib_dlls "${XSTUDIO_PORTABLE_STAGE}/lib/*.dll")
if(_root_lib_dlls)
message(FATAL_ERROR
"Portable packaging aborted: '${XSTUDIO_PORTABLE_STAGE}/lib' contains "
"runtime DLLs: ${_root_lib_dlls}. The portable step prunes that directory "
"because on Windows it is expected to hold only dev artifacts. Update "
"scripts/portable/portable.cmake.in if this layout change is intended.")
endif()

# 3. Prune dev-only content. Kept on purpose: *.pdb (crash triage), python/
# (root-level .py copy), share/xstudio/docs (Help menu), bin/python3 (the *.lib
# files inside it belong to the embedded python/numpy and stay as well).
set(_prune_paths
"${XSTUDIO_PORTABLE_STAGE}/include"
"${XSTUDIO_PORTABLE_STAGE}/extern"
"${XSTUDIO_PORTABLE_STAGE}/lib"
"${XSTUDIO_PORTABLE_STAGE}/share/xstudio/lib")
foreach(_path IN LISTS _prune_paths)
if(EXISTS "${_path}")
file(REMOVE_RECURSE "${_path}")
endif()
endforeach()

file(GLOB _dev_artifacts
"${XSTUDIO_PORTABLE_STAGE}/bin/*.lib"
"${XSTUDIO_PORTABLE_STAGE}/bin/*.exp")
if(_dev_artifacts)
file(REMOVE ${_dev_artifacts})
endif()

# 4. Launcher (start avoids leaving a console window on double-click,
# arguments are forwarded).
configure_file("${CMAKE_SOURCE_DIR}/scripts/portable/xstudio.bat.in"
"${XSTUDIO_PORTABLE_STAGE}/xstudio.bat" @ONLY)

# 5. README.
configure_file("${CMAKE_SOURCE_DIR}/scripts/portable/README.txt.in"
"${XSTUDIO_PORTABLE_STAGE}/README.txt" @ONLY)

# 6. Zip with a single top-level folder inside.
set(_portable_zip "${CMAKE_BINARY_DIR}/${XSTUDIO_PORTABLE_NAME}-portable.zip")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E tar cf "${_portable_zip}" --format=zip "${XSTUDIO_PORTABLE_NAME}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/portable"
RESULT_VARIABLE _zip_result)
if(NOT _zip_result EQUAL 0)
message(FATAL_ERROR
"Portable packaging failed: could not create '${_portable_zip}' "
"(exit code ${_zip_result}).")
endif()

message(STATUS "Portable xSTUDIO staged at: ${XSTUDIO_PORTABLE_STAGE}")
message(STATUS "Portable xSTUDIO zip: ${_portable_zip}")
4 changes: 4 additions & 0 deletions scripts/portable/xstudio.bat.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off
REM Portable launcher for xSTUDIO. Run xstudio.exe from the bin folder
REM next to this file; arguments are forwarded. Generated by CMake, do not edit.
start "" "%~dp0bin\xstudio.exe" %*
Loading