2020-03-23 09:03:24 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2020 Google LLC
|
|
|
|
#
|
|
|
|
# Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# https://llvm.org/LICENSE.txt
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
from enum import Enum
|
|
|
|
from git import Repo
|
|
|
|
import os
|
2020-04-16 12:25:24 +02:00
|
|
|
from pathlib import Path
|
2020-03-23 09:03:24 +01:00
|
|
|
import platform
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
2020-04-16 10:30:39 +02:00
|
|
|
import stat
|
2020-03-23 09:03:24 +01:00
|
|
|
from typing import List, Dict
|
|
|
|
import yaml
|
2020-04-16 10:30:39 +02:00
|
|
|
|
2020-03-23 09:03:24 +01:00
|
|
|
from choose_projects import ChooseProjects
|
|
|
|
|
|
|
|
|
|
|
|
class OperatingSystem(Enum):
|
|
|
|
Linux = 'linux'
|
|
|
|
Windows = 'windows'
|
|
|
|
|
|
|
|
|
|
|
|
class Configuration:
|
|
|
|
|
|
|
|
def __init__(self, config_file_path: str):
|
|
|
|
with open(config_file_path) as config_file:
|
|
|
|
config = yaml.load(config_file, Loader=yaml.SafeLoader)
|
|
|
|
self._environment = config['environment'] # type: Dict[OperatingSystem, Dict[str, str]]
|
|
|
|
self.general_cmake_arguments = config['arguments']['general'] # type: List[str]
|
|
|
|
self._specific_cmake_arguments = config['arguments'] # type: Dict[OperatingSystem, List[str]]
|
|
|
|
self._default_projects = config['default_projects'] # type: Dict[OperatingSystem, str]
|
|
|
|
self.operating_system = self._detect_os() # type: OperatingSystem
|
|
|
|
|
|
|
|
@property
|
|
|
|
def environment(self) -> Dict[str, str]:
|
|
|
|
return self._environment[self.operating_system.value]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def specific_cmake_arguments(self) -> List[str]:
|
|
|
|
return self._specific_cmake_arguments[self.operating_system.value]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def default_projects(self) -> str:
|
|
|
|
return self._default_projects[self.operating_system.value]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _detect_os() -> OperatingSystem:
|
|
|
|
"""Detect the current operating system."""
|
|
|
|
if platform.system() == 'Windows':
|
|
|
|
return OperatingSystem.Windows
|
|
|
|
return OperatingSystem.Linux
|
|
|
|
|
|
|
|
|
|
|
|
def _select_projects(config: Configuration, projects: str, repo_path: str) -> str:
|
|
|
|
"""select which projects to build.
|
|
|
|
|
|
|
|
if projects == "default", a default configuraiton will be used.
|
|
|
|
|
|
|
|
if project == "detect", ChooseProjects is used to magically detect the projects
|
|
|
|
based on the files modified in HEAD
|
|
|
|
"""
|
|
|
|
if projects == "default" or projects is None or len(projects) == 0:
|
|
|
|
return config.default_projects
|
|
|
|
if projects == "detect":
|
|
|
|
cp = ChooseProjects(repo_path)
|
|
|
|
repo = Repo('.')
|
|
|
|
patch = repo.git.diff("HEAD~1")
|
|
|
|
enabled_projects = ';'.join(cp.choose_projects(patch))
|
|
|
|
if enabled_projects is None or len(enabled_projects) == 0:
|
|
|
|
enabled_projects = 'all'
|
|
|
|
return enabled_projects
|
|
|
|
return projects
|
|
|
|
|
|
|
|
|
|
|
|
def _create_env(config: Configuration) -> Dict[str, str]:
|
|
|
|
"""Generate the environment variables for cmake."""
|
|
|
|
env = os.environ.copy()
|
|
|
|
env.update(config.environment)
|
|
|
|
return env
|
|
|
|
|
|
|
|
|
|
|
|
def _create_args(config: Configuration, llvm_enable_projects: str) -> List[str]:
|
|
|
|
"""Generate the command line arguments for cmake."""
|
|
|
|
arguments = [
|
|
|
|
os.path.join('..', 'llvm'),
|
|
|
|
'-D LLVM_ENABLE_PROJECTS="{}"'.format(llvm_enable_projects),
|
|
|
|
]
|
|
|
|
arguments.extend(config.general_cmake_arguments)
|
|
|
|
arguments.extend(config.specific_cmake_arguments)
|
|
|
|
|
|
|
|
# enable ccache if the path is set in the environment
|
|
|
|
if 'CCACHE_PATH' in os.environ:
|
|
|
|
arguments.extend([
|
|
|
|
'-D LLVM_CCACHE_BUILD=ON',
|
|
|
|
'-D LLVM_CCACHE_DIR={}'.format(os.environ['CCACHE_PATH']),
|
|
|
|
'-D LLVM_CCACHE_MAXSIZE=20G',
|
|
|
|
])
|
|
|
|
return arguments
|
|
|
|
|
|
|
|
|
2020-03-30 16:01:30 +02:00
|
|
|
def run_cmake(projects: str, repo_path: str, config_file_path: str = None, *, dryrun: bool = False):
|
2020-03-23 09:03:24 +01:00
|
|
|
"""Use cmake to configure the project.
|
|
|
|
|
|
|
|
This version works on all operating systems.
|
|
|
|
"""
|
|
|
|
if config_file_path is None:
|
|
|
|
script_dir = os.path.dirname(__file__)
|
|
|
|
config_file_path = os.path.join(script_dir, 'run_cmake_config.yaml')
|
|
|
|
config = Configuration(config_file_path)
|
|
|
|
|
|
|
|
build_dir = os.path.abspath(os.path.join(repo_path, 'build'))
|
2020-03-30 16:01:30 +02:00
|
|
|
if not dryrun:
|
2020-04-15 18:16:41 +02:00
|
|
|
secure_delete(build_dir)
|
2020-03-30 16:01:30 +02:00
|
|
|
os.makedirs(build_dir)
|
2020-03-23 09:03:24 +01:00
|
|
|
|
|
|
|
env = _create_env(config)
|
|
|
|
llvm_enable_projects = _select_projects(config, projects, repo_path)
|
2020-03-23 17:46:21 +01:00
|
|
|
print('Enabled projects: {}'.format(llvm_enable_projects))
|
2020-03-23 09:03:24 +01:00
|
|
|
arguments = _create_args(config, llvm_enable_projects)
|
|
|
|
cmd = 'cmake ' + ' '.join(arguments)
|
|
|
|
|
|
|
|
# On Windows: configure Visutal Studio before running cmake
|
|
|
|
if config.operating_system == OperatingSystem.Windows:
|
|
|
|
# FIXME: move this path to a config file
|
|
|
|
cmd = r'"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 && ' + cmd
|
2020-03-25 07:35:03 +01:00
|
|
|
|
|
|
|
print('Running cmake with these arguments:\n{}'.format(cmd))
|
2020-03-30 16:01:30 +02:00
|
|
|
if dryrun:
|
|
|
|
print('Dryrun, not invoking CMake!')
|
|
|
|
else:
|
|
|
|
subprocess.check_call(cmd, env=env, shell=True, cwd=build_dir)
|
|
|
|
_link_compile_commands(config, repo_path, build_dir)
|
2020-03-23 17:46:21 +01:00
|
|
|
|
|
|
|
|
2020-04-15 18:16:41 +02:00
|
|
|
def secure_delete(path: str):
|
|
|
|
"""Try do delete a local folder.
|
|
|
|
|
2020-04-16 10:30:39 +02:00
|
|
|
Handle read-only files.
|
2020-04-15 18:16:41 +02:00
|
|
|
"""
|
2020-04-16 10:30:39 +02:00
|
|
|
if not os.path.exists(path):
|
|
|
|
return
|
|
|
|
|
|
|
|
def del_rw(action, name, exc):
|
2020-04-16 13:54:50 +02:00
|
|
|
os.chmod(name, stat.S_IWRITE)
|
|
|
|
os.unlink(name)
|
2020-04-16 10:30:39 +02:00
|
|
|
|
|
|
|
shutil.rmtree(path, onerror=del_rw)
|
2020-04-15 18:16:41 +02:00
|
|
|
|
|
|
|
|
2020-03-23 17:46:21 +01:00
|
|
|
def _link_compile_commands(config: Configuration, repo_path: str, build_dir: str):
|
|
|
|
"""Link compile_commands.json from build to root dir"""
|
|
|
|
if config.operating_system != OperatingSystem.Linux:
|
|
|
|
return
|
|
|
|
source_path = os.path.join(build_dir, 'compile_commands.json')
|
|
|
|
target_path = os.path.join(repo_path, 'compile_commands.json')
|
|
|
|
if os.path.exists(target_path):
|
|
|
|
os.remove(target_path)
|
|
|
|
os.symlink(source_path, target_path)
|
|
|
|
|
2020-03-23 09:03:24 +01:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser(description='Run CMake for LLVM.')
|
|
|
|
parser.add_argument('projects', type=str, nargs='?', default='default')
|
|
|
|
parser.add_argument('repo_path', type=str, nargs='?', default=os.getcwd())
|
2020-03-30 16:01:30 +02:00
|
|
|
parser.add_argument('--dryrun', action='store_true')
|
2020-03-23 09:03:24 +01:00
|
|
|
args = parser.parse_args()
|
2020-03-30 16:01:30 +02:00
|
|
|
run_cmake(args.projects, args.repo_path, dryrun=args.dryrun)
|