summaryrefslogtreecommitdiff
path: root/lib/spack/spack/build_systems/msbuild.py
blob: 4de8c86e0ac28cda26cf575ebbf896720164a1f3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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


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")


@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 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)]

    def define_targets(self, *targets):
        return "/target:" + ";".join(targets) if targets else ""

    def define(self, msbuild_arg, value):
        return "/p:{}={}".format(msbuild_arg, value)

    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 []

    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()

    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),
            )

    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)
            )