Source code for spack.build_systems.msbuild

# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
from typing import List  # novm

import llnl.util.filesystem as fs

import spack.builder
import spack.package_base
from spack.directives import build_system, conflicts

from ._checks import BaseBuilder


[docs] class MSBuildPackage(spack.package_base.PackageBase): """Specialized class for packages built using Visual Studio project files or solutions.""" #: This attribute is used in UI queries that need to know the build #: system base class build_system_class = "MSBuildPackage" build_system("msbuild") conflicts("platform=linux", when="build_system=msbuild") conflicts("platform=darwin", when="build_system=msbuild") conflicts("platform=cray", when="build_system=msbuild")
[docs] @spack.builder.builder("msbuild") class MSBuildBuilder(BaseBuilder): """The MSBuild builder encodes the most common way of building software with Mircosoft's MSBuild tool. It has two phases that can be overridden, if need be: 1. :py:meth:`~.MSBuildBuilder.build` 2. :py:meth:`~.MSBuildBuilder.install` It is usually necessary to override the :py:meth:`~.MSBuildBuilder.install` phase as many packages with MSBuild systems neglect to provide an install target. The default install phase will attempt to invoke an install target from MSBuild. If none exists, this will result in a build failure For a finer tuning you may override: +-----------------------------------------------+---------------------+ | **Method** | **Purpose** | +===============================================+=====================+ | :py:attr:`~.MSBuildBuilder.build_targets` | Specify ``msbuild`` | | | targets for the | | | build phase | +-----------------------------------------------+---------------------+ | :py:attr:`~.MSBuildBuilder.install_targets` | Specify ``msbuild`` | | | targets for the | | | install phase | +-----------------------------------------------+---------------------+ | :py:meth:`~.MSBuildBuilder.build_directory` | Directory where the | | | project sln/vcxproj | | | is located | +-----------------------------------------------+---------------------+ """ phases = ("build", "install") #: Targets for ``make`` during the :py:meth:`~.MSBuildBuilder.build` phase build_targets: List[str] = [] #: Targets for ``msbuild`` during the :py:meth:`~.MSBuildBuilder.install` phase install_targets: List[str] = ["INSTALL"] @property def build_directory(self): """Return the directory containing the MSBuild solution or vcxproj.""" return fs.windows_sfn(self.pkg.stage.source_path) @property def toolchain_version(self): """Return currently targeted version of MSVC toolchain Override this method to select a specific version of the toolchain or change selection heuristics. Default is whatever version of msvc has been selected by concretization""" return "v" + self.pkg.compiler.platform_toolset_ver @property def std_msbuild_args(self): """Return common msbuild cl arguments, for now just toolchain""" return [self.define("PlatformToolset", self.toolchain_version)]
[docs] def define_targets(self, *targets): return "/target:" + ";".join(targets) if targets else ""
[docs] def define(self, msbuild_arg, value): return "/p:{}={}".format(msbuild_arg, value)
[docs] def msbuild_args(self): """Define build arguments to MSbuild. This is an empty list by default. Individual packages should override to specify MSBuild args to command line PlatformToolset is already defined an can be controlled via the `toolchain_version` property""" return []
[docs] def msbuild_install_args(self): """Define install arguments to MSBuild outside of the INSTALL target. This is the same as `msbuild_args` by default.""" return self.msbuild_args()
[docs] def build(self, pkg, spec, prefix): """Run "msbuild" on the build targets specified by the builder.""" with fs.working_dir(self.build_directory): inspect.getmodule(self.pkg).msbuild( *self.std_msbuild_args, *self.msbuild_args(), self.define_targets(*self.build_targets), )
[docs] def install(self, pkg, spec, prefix): """Run "msbuild" on the install targets specified by the builder. This is INSTALL by default""" with fs.working_dir(self.build_directory): inspect.getmodule(self.pkg).msbuild( *self.msbuild_install_args(), self.define_targets(*self.install_targets) )