Skip to content
Merged
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 src/cmake/fancy_add_executable.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ macro (fancy_add_executable)
check_is_enabled (${_target_NAME} _target_NAME_enabled)
if (_target_NAME_enabled)
add_executable (${_target_NAME} ${_target_SRC})
if (WIN32)
# Disable default manifest generation to avoid conflicts.
target_link_options(${_target_NAME} PRIVATE "/MANIFEST:NO")
# Include Windows resource file, which will in turn embed our exe manifest.
target_sources(${_target_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src/windows/oiio_exe.rc")
endif ()
target_include_directories (${_target_NAME} PRIVATE ${_target_INCLUDE_DIRS})
target_include_directories (${_target_NAME} SYSTEM PRIVATE ${_target_SYSTEM_INCLUDE_DIRS})
target_compile_definitions (${_target_NAME} PRIVATE
Expand Down
22 changes: 20 additions & 2 deletions src/cmake/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,31 @@ macro (oiio_add_all_tests)
ENABLEVAR ENABLE_TARGA
IMAGEDIR oiio-images)
endif()
if (NOT WIN32)
if (WIN32)
if (OIIO_BUILD_TOOLS)
# Add test for long path handling if support is enabled at the system level.
execute_process (
COMMAND ${Python3_EXECUTABLE} "${CMAKE_SOURCE_DIR}/testsuite/windows-long-paths/check_registry.py"
RESULT_VARIABLE _reg_check_status
OUTPUT_QUIET ERROR_QUIET
)
if (_reg_check_status EQUAL 0)
add_test (NAME windows-long-paths
COMMAND
${Python3_EXECUTABLE} "${CMAKE_SOURCE_DIR}/testsuite/windows-long-paths/test.py"
--source-root "${CMAKE_SOURCE_DIR}"
--oiiotool-path $<TARGET_FILE:oiiotool>)
else ()
message (STATUS "Skipping Windows long path test: System-level support is not enabled")
endif ()
endif ()
else ()
oiio_add_tests (term
ENABLEVAR ENABLE_TERM)
# I just could not get this test to work on Windows CI. The test fails
# when comparing the output, but the saved artifacts compare just fine
# on my system. Maybe someone will come back to this.
endif ()
endif ()
oiio_add_tests (tiff-suite tiff-depths tiff-misc
IMAGEDIR oiio-images/libtiffpic)
oiio_add_tests (webp
Expand Down
15 changes: 15 additions & 0 deletions src/windows/oiio_exe.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
</assembly>
6 changes: 6 additions & 0 deletions src/windows/oiio_exe.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Resource script for OIIO EXEs.

// The value is invariant, so should be safe to hardcode, but we could replace
// 24 with `RT_MANIFEST` by adding `#include "winres.h"` first.
Comment on lines +3 to +4
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I don't have a strong instinct for. The manifest resource ID isn't going to change, and this value hardcoding seems to be fairly commonplace from what I've found. I opted to avoid an #include here to start just because I don't know if that would have any other implications.

// Reference: https://learn.microsoft.com/en-us/windows/win32/menurc/user-defined-resource
1 24 "oiio_exe.manifest"
36 changes: 36 additions & 0 deletions testsuite/windows-long-paths/check_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Helper script to detect system-level long path support on Windows.

Returns 0 if the long path support is enabled in the registry, or 1 otherwise.

Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry#registry-setting-to-enable-long-paths
"""

# Copyright Contributors to the OpenImageIO project.
# SPDX-License-Identifier: Apache-2.0
# https://github.com/AcademySoftwareFoundation/OpenImageIO

import sys
import winreg

_SUB_KEY = "SYSTEM\\CurrentControlSet\\Control\\FileSystem"
_VALUE_NAME = "LongPathsEnabled"


def main() -> int:
try:
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _SUB_KEY) as key:
reg_value, value_type = winreg.QueryValueEx(key, _VALUE_NAME)
except OSError:
# Key does not exist
return 1

# It's vanishingly unlikely that someone would stuff some other value type
# in this key, but let's be paranoid.
if value_type != winreg.REG_DWORD:
return 1

return int(reg_value != 1)


if __name__ == "__main__":
sys.exit(main())
56 changes: 56 additions & 0 deletions testsuite/windows-long-paths/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Test script for long path support in compiled Windows executables.

This script verifies that `oiiotool` can read paths longer than MAX_PATH (260)
characters on Windows.

Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
"""

# Copyright Contributors to the OpenImageIO project.
# SPDX-License-Identifier: Apache-2.0
# https://github.com/AcademySoftwareFoundation/OpenImageIO

import argparse
import os
import shutil
import subprocess
import sys
import tempfile
from uuid import uuid4

_WINDOWS_MAX_PATH = 260


def main(src_root: str, oiiotool_exe: str) -> int:
"""
Manufactures a long directory path within a temp. directory, copies a test
image to it, and ensures `oiiotool` can read it without erroring.
"""
if not os.path.isfile(oiiotool_exe):
print(f"ERROR: oiiotool path {oiiotool_exe!r} does not exist")
return 1

test_image = "grid.tif"
src_image_path = os.path.join(src_root, "testsuite", "common", test_image)

with tempfile.TemporaryDirectory() as test_dir:
while len(test_dir) < _WINDOWS_MAX_PATH:
test_dir = os.path.join(test_dir, str(uuid4()))
os.makedirs(test_dir, exist_ok=True)

shutil.copy(src_image_path, test_dir)
test_image_path = os.path.join(test_dir, test_image)

print("Testing long image path:", test_image_path, flush=True)
result = subprocess.call([oiiotool_exe, test_image_path, "--printinfo"])

return result


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Windows long path test")
parser.add_argument("--source-root", required=True)
parser.add_argument("--oiiotool-path", required=True)
args = parser.parse_args()

sys.exit(main(args.source_root, args.oiiotool_path))
Loading