Source code for symforce.path_util
# ----------------------------------------------------------------------------
# SymForce - Copyright 2022, Skydio, Inc.
# This source code is under the Apache 2.0 license found in the LICENSE file.
# ----------------------------------------------------------------------------
import json
import os
import typing as T
from pathlib import Path
[docs]
class MissingManifestException(RuntimeError):
pass
class _Manifest:
"""
Internal class to manage loading data from the build manifest and caching that data. Not
intended for use outside of path_util.py.
"""
_manifest: T.Optional[T.Dict[str, str]] = None
@classmethod
def _ensure_loaded(cls) -> None:
if cls._manifest is None:
manifest_path = Path(__file__).parent.parent / "build" / "manifest.json"
try:
with open(manifest_path) as f:
cls._manifest = json.load(f)
except FileNotFoundError as ex:
raise MissingManifestException(f"Manifest not found at {manifest_path}") from ex
@classmethod
def get_entry(cls, key: str) -> Path:
cls._ensure_loaded()
assert cls._manifest is not None
return Path(cls._manifest[key]).resolve()
@classmethod
def get_entries(cls, key: str) -> T.List[Path]:
cls._ensure_loaded()
assert cls._manifest is not None
return [Path(s).resolve() for s in cls._manifest[key]]
[docs]
def symforce_dir() -> Path:
return Path(__file__).parent.parent
[docs]
def symenginepy_install_dir() -> Path:
return _Manifest.get_entry("symenginepy_install_dir")
[docs]
def cc_sym_install_dir() -> Path:
return _Manifest.get_entry("cc_sym_install_dir")
[docs]
def binary_output_dir() -> Path:
return _Manifest.get_entry("binary_output_dir")
[docs]
def symforce_root() -> Path:
"""
The root directory of the symforce project
"""
return Path(__file__).parent.parent
[docs]
def eigen_lcm_lcmtypes_dir() -> T.Optional[Path]:
"""
Directory of the eigen_lcm ``.lcm`` sources, or ``None`` if not found.
Codegen feeds these to skymarshal's recursive-hash precompute. Standalone builds keep them under
``third_party/eigen_lcm``; Bazel sets ``SYMFORCE_EIGEN_LCM_LCMTYPES_DIR`` (absolute, or relative
to the runfiles tree) to point elsewhere.
"""
override = os.environ.get("SYMFORCE_EIGEN_LCM_LCMTYPES_DIR")
if override:
candidates = [Path(override)]
runfiles_dir = os.environ.get("RUNFILES_DIR") or os.environ.get("TEST_SRCDIR")
if runfiles_dir and not os.path.isabs(override):
candidates.append(Path(runfiles_dir) / "_main" / override)
candidates.append(Path(runfiles_dir) / override)
for candidate in candidates:
if candidate.is_dir():
return candidate
candidate = symforce_root() / "third_party" / "eigen_lcm" / "lcmtypes"
return candidate if candidate.is_dir() else None
[docs]
def symforce_data_root(test_file_path: T.Optional[str]) -> Path:
"""
The root directory of the symforce project, for use accessing data that might need to be updated
(such as generated files). Most of the time this is the same as :func:`symforce_root`, except
for two cases:
1) When running tests on wheels, this is the root of the copy of SymForce containing the test,
not the installed copy. The generated files we're checking aren't in the wheel, they're in
the source tree.
2) When the ``--update`` flag is passed to a test, this is guaranteed to point to the *resolved*
version, i.e. the actual symforce location on disk regardless of whether this path is a
symlink.
"""
from symforce.test_util.test_case_mixin import SymforceTestCaseMixin
if test_file_path is not None and "SYMFORCE_WHEEL_TESTS" in os.environ:
return Path(test_file_path).resolve().parent.parent
if SymforceTestCaseMixin.should_update():
return Path(__file__).resolve().parent.parent
else:
return symforce_root()