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
31 changes: 18 additions & 13 deletions src/rez/build_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import os.path

from rez.rex import literal
from rez.build_process import BuildType
from rez.exceptions import BuildSystemError
from rez.packages import get_developer_package
Expand Down Expand Up @@ -225,27 +226,31 @@ def set_standard_vars(cls, executor, context, variant, build_type, install,
'REZ_BUILD_PATH': executor.normalize_path(build_path),
'REZ_BUILD_THREAD_COUNT': package.config.build_thread_count,
'REZ_BUILD_VARIANT_INDEX': variant.index or 0,
'REZ_BUILD_VARIANT_REQUIRES': ' '.join(variant_requires),
'REZ_BUILD_VARIANT_REQUIRES': literal(' '.join(variant_requires)),
'REZ_BUILD_VARIANT_SUBPATH': executor.normalize_path(variant_subpath),
'REZ_BUILD_PROJECT_VERSION': str(package.version),
'REZ_BUILD_PROJECT_NAME': package.name,
'REZ_BUILD_PROJECT_DESCRIPTION': (package.description or '').strip(),
'REZ_BUILD_PROJECT_FILE': package.filepath,
'REZ_BUILD_SOURCE_PATH': executor.normalize_path(
os.path.dirname(package.filepath)
'REZ_BUILD_PROJECT_VERSION': literal(str(package.version)),
'REZ_BUILD_PROJECT_NAME': literal(package.name),
'REZ_BUILD_PROJECT_DESCRIPTION': literal((package.description or '').strip()),
'REZ_BUILD_PROJECT_FILE': literal(package.filepath),
'REZ_BUILD_SOURCE_PATH': literal(
executor.normalize_path(os.path.dirname(package.filepath))
),
'REZ_BUILD_REQUIRES': ' '.join(
str(x) for x in context.requested_packages(True)
'REZ_BUILD_REQUIRES': literal(
' '.join(
str(x) for x in context.requested_packages(True)
)
),
'REZ_BUILD_REQUIRES_UNVERSIONED': ' '.join(
x.name for x in context.requested_packages(True)
'REZ_BUILD_REQUIRES_UNVERSIONED': literal(
' '.join(
x.name for x in context.requested_packages(True)
)
),
'REZ_BUILD_TYPE': build_type.name,
'REZ_BUILD_TYPE': literal(build_type.name),
'REZ_BUILD_INSTALL': 1 if install else 0,
}

if install_path:
vars_['REZ_BUILD_INSTALL_PATH'] = executor.normalize_path(install_path)
vars_['REZ_BUILD_INSTALL_PATH'] = literal(executor.normalize_path(install_path))

if config.rez_1_environment_variables and \
not config.disable_rez_1_compatibility and \
Expand Down
77 changes: 75 additions & 2 deletions src/rez/tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
test the build system
"""
from rez.config import config
from rez.build_process import create_build_process
from rez.build_system import create_build_system
from rez.build_process import create_build_process, BuildType
from rez.build_system import create_build_system, BuildSystem
from rez.resolved_context import ResolvedContext
from rez.exceptions import BuildError, BuildContextResolveError, \
PackageFamilyNotFoundError
import unittest
from rez.tests.util import TestBase, TempdirMixin, find_file_in_path, \
per_available_shell, install_dependent, program_dependent
from rez.utils.platform_ import platform_
from rez.shells import create_shell
from rez.packages import get_developer_package
from rez.rex import RexExecutor
import shutil
import os.path

Expand Down Expand Up @@ -200,6 +203,76 @@ def test_build_custom(self):
stdout = proc.communicate()[0]
self.assertEqual('Oh hai!', stdout.decode("utf-8").strip())

def test_set_standard_vars_escaping(self):
"""Test that set_standard_vars properly escapes environment variables."""
# Create a test package directory with special characters in description
temp_pkg_dir = os.path.join(self.root, "test_special_package")
os.makedirs(temp_pkg_dir)

# Create a package.py file with special characters
package_py_content = """
name = "test_special_chars"
version = "1.0.0"
description = 'A test package with "quotes" and $pecial characters & more!'
authors = ["[email protected]"]
requires = []
"""

package_py_path = os.path.join(temp_pkg_dir, "package.py")
with open(package_py_path, 'w') as f:
f.write(package_py_content)

# Get the developer package
package = get_developer_package(temp_pkg_dir)

# Get the first variant from the package
variant = next(package.iter_variants())

# Create a minimal context for testing - we don't need to resolve packages
# since we're only testing environment variable escaping
context = self._create_context() # Empty context

# Create a bash shell executor using RexExecutor
bash_shell = create_shell("bash")
executor = RexExecutor(interpreter=bash_shell, parent_environ={}, shebang=False)

build_path = os.path.join(self.root, "build")
install_path = os.path.join(self.root, "install")

BuildSystem.set_standard_vars(
executor=executor,
context=context,
variant=variant,
build_type=BuildType.local,
install=True,
build_path=build_path,
install_path=install_path
)

# Get the generated shell script
script_output = executor.get_output()

self.assertEqual(
script_output,
f"""export REZ_BUILD_ENV="1"
export REZ_BUILD_PATH="{build_path}"
export REZ_BUILD_THREAD_COUNT="{package.config.build_thread_count}"
export REZ_BUILD_VARIANT_INDEX="0"
export REZ_BUILD_VARIANT_REQUIRES=''
export REZ_BUILD_VARIANT_SUBPATH=""
export REZ_BUILD_PROJECT_VERSION='1.0.0'
export REZ_BUILD_PROJECT_NAME='test_special_chars'
export REZ_BUILD_PROJECT_DESCRIPTION='A test package with "quotes" and $pecial characters & more!'
export REZ_BUILD_PROJECT_FILE='{package_py_path}'
export REZ_BUILD_SOURCE_PATH='{temp_pkg_dir}'
export REZ_BUILD_REQUIRES=''
export REZ_BUILD_REQUIRES_UNVERSIONED=''
export REZ_BUILD_TYPE='local'
export REZ_BUILD_INSTALL="1"
export REZ_BUILD_INSTALL_PATH='{install_path}'
"""
)


if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions src/rezplugins/build_system/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ def _actions_callback(executor):
value = list(map(str, value))
value = list(map(quote, value))
value = ' '.join(value)
else:
value = quote(str(value))
Copy link
Member Author

@JeanChristopheMorinPerso JeanChristopheMorinPerso May 19, 2025

Choose a reason for hiding this comment

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

This protects against cases where a user passes something like

rez-build --myarg '$(run command)'

We were already doing that for elements in sequences anyway.


executor.env[varname] = value

Expand Down
Loading