Compare commits

...
Author SHA1 Message Date
John Colvin c5b79b3dae chore: Define common IUniqueID member impls in interface
Taking advantage of nifty new language feats
2023-08-22 11:35:41 -04:00
John Colvin 5180d13fb0 build (refactor): Organize py classes a bit 2023-08-22 11:35:41 -04:00
John Colvin 4065b8b4c4 dev: Simplify sparkd build instructions
Restructure in style similar to Spark and Api Server build instructions
2023-08-22 11:35:41 -04:00
John Colvin a28070713e dev: Add instructions to build spark in README 2023-08-22 11:35:41 -04:00
John Colvin 83cf96d254 build: Replace build_api_server.py w/ cmake/dotnet clis
Add instructions to build the Api Server in README
2023-08-22 11:35:41 -04:00
John Colvin 56b3eb60d5 build: Remove 'jobs' arg from CMake build presets
Modern CMake uses an appropriate number of jobs by default (I believe
the number of cores on the cpu)
2023-08-22 01:29:45 -04:00
John Colvin c30e09a6eb build: Init SPARK_ENABLE_API_SERVER reguardless of platform 2023-08-22 01:29:45 -04:00
John Colvin be96cbb57d build: Only build Admin Web App on Linux 2023-08-22 01:29:45 -04:00
John Colvin d26b818939 build: Copy libspark to dotnet bin dirs in CMakeLists 2023-08-22 01:29:45 -04:00
15 changed files with 546 additions and 641 deletions

No files matched your search

+6 -2
View File
@@ -20,16 +20,21 @@ set(SPARK_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
set(BUILD_API_SERVER OFF CACHE BOOL "Build the api server")
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
if (WIN32)
set(BUILD_SPARKD OFF CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP OFF CACHE BOOL "Build the Admin WebApp")
else()
set(BUILD_SPARKD ON CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
endif()
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${SPARK_LIB})
if(BUILD_API_SERVER)
add_compile_definitions(SPARK_ENABLE_API_SERVER)
endif()
if(WIN32)
# flags
set(
@@ -66,7 +71,6 @@ else()
set(SPARK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/$<CONFIG>")
if(BUILD_API_SERVER)
add_compile_definitions(SPARK_ENABLE_API_SERVER)
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/spark_api/")
add_compile_definitions(SHARED_MEMORY_PREFIX="spark_api")
elseif(BUILD_SPARKD)
+25 -26
View File
@@ -154,7 +154,9 @@
"BUILD_API_SERVER": {
"type": "bool",
"value": "ON"
}
},
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
"DOTNET_WINDOWS_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0-windows"
}
},
@@ -215,8 +217,7 @@
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"hidden": true,
"jobs": 4
"hidden": true
},
{
@@ -243,7 +244,6 @@
"rhs": "Linux"
},
"hidden": true,
"jobs": 8,
"targets": [
"data_files_dir",
"admin_web_app_bundle",
@@ -284,53 +284,52 @@
},
{
"name": "spark-api-server-debug-windows",
"name": "spark-api-server-windows",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"configurePreset": "spark-api-server-windows",
"hidden": true,
"configurePreset": "spark-api-server-windows"
},
{
"name": "spark-api-server-windows-debug",
"displayName": "Spark Api Server (Debug)",
"inherits": "spark",
"inherits": "spark-api-server-windows",
"configuration": "Debug"
},
{
"name": "spark-api-server-release-windows",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"configurePreset": "spark-api-server-windows",
"name": "spark-api-server-windows-release",
"displayName": "Spark Api Server (Debug)",
"inherits": "spark",
"inherits": "spark-api-server-windows",
"configuration": "Release"
},
{
"name": "spark-api-server-debug-linux",
"name": "spark-api-server-linux",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"configurePreset": "spark-api-server-linux",
"hidden": true,
"configurePreset": "spark-api-server-linux"
},
{
"name": "spark-api-server-linux-debug",
"inherits": "spark-api-server-linux",
"displayName": "Spark Api Server (Debug)",
"inherits": "spark",
"configuration": "Debug"
},
{
"name": "spark-api-server-release-linux",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"configurePreset": "spark-api-server-linux",
"displayName": "Spark Api Server (Debug)",
"name": "spark-api-server-linux-release",
"inherits": "spark-api-server-linux",
"displayName": "Spark Api Server (Release)",
"configuration": "Release"
},
+188 -41
View File
@@ -1,8 +1,15 @@
[cmake]: https://cmake.org/
[cmake ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
[cmake presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
[cpp ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
[clangd ext]: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd
[dotnet]: https://dotnet.microsoft.com/en-us/download/dotnet
[ninja build]: https://ninja-build.org/
[ninja multi-config generator]: https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html
[python]: https://www.python.org/
[py venv]: https://docs.python.org/3/library/venv.html
[visual studio]: https://visualstudio.microsoft.com/
[visual studio generator]: https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html
# C++ Intellisense for Spark on Windows (VS Code)
**Steps**
@@ -30,47 +37,187 @@
* *The **Clang** presets may work best with the **Clangd** extension*
* *Intellisense won't work for generated files/types until you build libspark*
# Build Sparkd on Linux
# Build Spark on Windows
### Dependencies
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Visual Studio][visual studio]** (2022)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
**Notes**
* Sparkd is built using **[CMakePresets][cmake presets]**
### References
* **[CMakePresets][cmake presets]**
* **[Visual Studio 17 2022 (CMake generator)][visual studio generator]**
* **[Python Virtual Environment][py venv]**
### Notes
* *Run commands from the repo root dir unless otherwise noted*
**Steps**
1. Set up the **[Python virtual environment][py venv]**<br>
* *Run commands from the repo root dir unless otherwise noted*
1. Create the virtual environment
```
python3 -m venv .venv
```
2. Activate the virtual environment
```
. .venv/bin/activate
```
3. Verify `python` in the venv is first in the path<br>
* ***Expected:** output shows the absolute path to `.venv/bin/python`*
```
which python
```
4. Update `pip`<br>
* *The venv module initializes virtual environments with its own version
of pip, rather than the system's version. Even if the latter is up-to-date, the
former may not be.*
```
python -m pip install --upgrade pip
```
5. Install the packages listed `requirements.txt`
```
pip install -r requirements.txt
```
2. Build **Sparkd**<br>
* From the terminal
```
tools/build_sparkd.sh
```
* *Run with `--help` to see available options*
* From VS Code
1. Install the **[CMake extension][cmake ext]**
2. In the VS Code Command Palette, run **CMake: Select Configure
Preset**, then select the **[preset][cmake presets]** you wish to
build from the dropdown
3. Run **CMake: Build** in the command palette.
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
.venv\bin\activate.bat
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run commands in python virtual environment*
```
# Configure
cmake --preset spark
# Build Debug
cmake --build --preset spark-debug
dotnet build spark_client.sln --configuration Debug
# Build Release
cmake --build --preset spark-release
dotnet build spark_client.sln --configuration Release
```
# Build Sparkd on Linux
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Ninja Build][ninja build]**
* **[Python][python]** (version >= 3.9)
* **[Docker][docker]**
### **References**
* **[CMakePresets][cmake presets]**
* **[Ninja (CMake generator)][ninja generator]**
* **[Python Virtual Environment][py venv]**
* **[Devcontainer](devcontainer)**
### Prerequisites
* Open repository in a VS Code [Devcontainer](../docker/sparkd/dev/README.md), or
create your own [docker container](../docker/sparkd/dev/README.md) in which to build
### Notes
* *Run commands from the repo root dir unless otherwise noted*<br>
1. Set up the **[Python virtual environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
. .venv/bin/activate
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run commands in python virtual environment*
```
# Configure clang debug
cmake --preset sparkd-clang-debug
# Build clang debug
cmake --build --preset spark-clang-debug
# Configure gcc release
cmake --build --preset sparkd-gcc-release
# Build gcc release
cmake --build --preset sparkd-gcc-release
```
# Build Spark Api Server on Windows
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Visual Studio][visual studio]** (version)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
### **References**
* **[CMakePresets][cmake presets]**
* **[Visual Studio 17 2022 (CMake generator)][visual studio generator]**
* **[Python Virtual Environment][py venv]**
### Notes
* *Run commands from the repo root dir unless otherwise noted*
1. Set up the **[Python Virtual Environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
.venv\bin\activate.bat
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run in python virtual environment*
```
# Configure
cmake --preset spark-api-server-windows
# Build debug
cmake --build --preset spark-api-server-windows-debug
dotnet build Spark.Api.Server.sln --configuration Debug
# Build release
cmake --build --preset spark-api-server-windows-release
dotnet build Spark.Api.Server.sln --configuration Release
```
# Build Spark Api Server on Linux
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
### **References**
* **[CMakePresets][cmake presets]**
* **[Ninja Multi-Config (CMake generator)][ninja multi-config generator]**
* **[Python Virtual Environment][py venv]**
### Prerequisites
* Open repository in a VS Code [Devcontainer](../docker/sparkd/dev/README.md), or
create your own [docker container](../docker/sparkd/dev/README.md) in which to build
### Notes
* *Run commands from the repo root dir unless otherwise noted*
1. Set up the **[Python Virtual Environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
. .venv/bin/activate
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run in python virtual environment*
```
# Configure
cmake --preset spark-api-server-linux
# Build debug
cmake --build --preset spark-api-server-linux-debug
dotnet build Spark.Api.Server.sln --configuration Debug
# Build release
cmake --build --preset spark-api-server-linux-release
dotnet build Spark.Api.Server.sln --configuration Release
```
+12 -10
View File
@@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Represents a unique client, a little redundant
public interface IUniqueID {
public string ID { get; }
public int GetHashCode();
public string ToString();
public bool Equals(object? obj);
public interface IUniqueID : IEquatable<object?> {
string ID { get; }
public int GetHashCode() =>
ID.GetHashCode();
public string? ToString() =>
ID;
bool IEquatable<object?>.Equals(object? obj) =>
obj is IUniqueID other && ID == other.ID;
}
}
}
+3 -22
View File
@@ -1,25 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Represents a unique client, a little redundant
public class UniqueID : IUniqueID {
public string ID { get; }
public UniqueID(string id) {
ID = id;
}
public override int GetHashCode() {
return this.ID.GetHashCode();
}
public override string ToString() {
return this.ID.ToString() ?? "";
}
public override bool Equals(object? obj) {
var other = obj == null ? null : obj as IUniqueID;
return other != null && (string)other.ID == ID;
}
public class UniqueID(string id) : IUniqueID {
public string ID { get; } = id;
}
}
}
+18
View File
@@ -6,6 +6,10 @@ include(${CMAKE_SOURCE_DIR}/cmake/Protobuf.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateDefaultConfig.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/PrintList.cmake)
if (DEFINED DOTNET_BINARY_DIR OR DEFINED DOTNET_WINDOWS_BINARY_DIR)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddCopyBinariesTarget.cmake)
endif()
if(WIN32)
if(BUILD_SPARKD)
message(FATAL_ERROR "Cannot build sparkd on Windows (Please set BUILD_SPARKD to False)")
@@ -53,6 +57,20 @@ get_link_libraries(_link_libs)
print_list(_link_libs VERBOSE)
target_link_libraries(${PROJECT_NAME} ${_link_libs})
# Copy binaries to Dotnet build dirs (for csprojs that depend on libspark)
if (DEFINED DOTNET_BINARY_DIR)
add_copy_binaries_target(
copy_to_dotnet_binary_dir
${DOTNET_BINARY_DIR}
"Copy ${PROJECT_NAME} bins to Dotnet build dir")
endif()
if (DEFINED DOTNET_WINDOWS_BINARY_DIR)
add_copy_binaries_target(
copy_to_dotnet_windows_binary_dir
${DOTNET_WINDOWS_BINARY_DIR}
"Copy ${PROJECT_NAME} bins to Dotnet Windows build dir")
endif()
if(BUILD_SPARKD)
install(TARGETS ${PROJECT_NAME} DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
endif()
+16
View File
@@ -0,0 +1,16 @@
function(add_copy_binaries_target tgt_name dest_dir)
set(_output
"${dest_dir}/${PROJECT_NAME}.dll"
"$<$<CONFIG:Debug>:${dest_dir}/${PROJECT_NAME}.pdb>")
add_custom_command(
OUTPUT ${_output}
COMMAND ${CMAKE_COMMAND}
ARGS -E copy
$<$<CONFIG:Debug>:${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${PROJECT_NAME}.pdb>
${dest_dir}/${PROJECT_NAME}.pdb
COMMAND ${CMAKE_COMMAND}
ARGS -E copy
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${PROJECT_NAME}.dll
${dest_dir}/${PROJECT_NAME}.dll)
add_custom_target(${tgt_name} ALL DEPENDS ${_output} COMMENT ${ARGN})
endfunction()
-245
View File
@@ -1,245 +0,0 @@
import argparse
import subprocess
import os
from typing import Optional
from cmake.cmake_utils import CMakeCompilerPair, CMakeConfigureCommand
from core.argparse_utils import add_toggle_opt, get_arg
from core.file_utils import CWD, SparkPaths, as_path, create_dir, link_file
from core.iter_utils import flatten, is_none_or_empty
from core.logging import exit_msg
def _parse_compiler(arg: str or None):
'''Parses compiler input (from cli) and returns a tuple of c, cxx compilers respectively'''
known_compilers = {
'gcc': CMakeCompilerPair('gcc', 'gcc-11', 'g++-11'),
'clang': CMakeCompilerPair('clang', 'clang', 'clang++'),
'clang-13': CMakeCompilerPair('clang', 'clang-13', 'clang++-13'),
}
if arg in known_compilers:
return known_compilers[arg]
raise ValueError(
f"Unexpected compiler '{arg}'. Please use one of [{' '.join(known_compilers)}]"
)
def _parse_build_dir(build_dir, build_type: Optional[str] = None):
if build_dir is not None:
return as_path(build_dir).value()
rv = SparkPaths.root().joinpath('build')
if build_type is not None:
rv = rv.joinpath(build_type)
return rv
class BuildApiServerArgs:
def __init__(self, args):
args_dict = vars(args)
self.compiler = _parse_compiler(get_arg(args_dict, 'compiler', 'gcc'))
# Forcibly correct case (so user doesn't have to worry abt it)
self.build_type = args.build_type.title()
if self.build_type not in ['Debug', 'Release']:
raise ValueError(f"Unexpected build_type '{args.build_type}'")
self.cmake_build_dir = _parse_build_dir(
args_dict.get('cmake_output_dir')).absolute()
self.clean = args.clean
self.jobs = args.jobs
self.targets = None if args.target is None else flatten(args.target)
self.dotnet_verbosity = args.verbosity
# Translate dotnet verbosity level to binary '--verbose' flag for cmake
self.cmake_verbose = args.verbosity in [
'd', 'detailed', 'diag', 'diagnostic'
]
def _get_arg_parser():
parser = argparse.ArgumentParser(description='Build spark_client.sln')
parser.add_argument(
'--build-type',
metavar='debug|release',
type=str,
nargs='?',
help='The build type')
parser.add_argument(
'--compiler',
metavar='gcc|clang',
type=str,
nargs='?',
help='The compiler')
parser.add_argument(
'-o',
'--cmake-output-dir',
metavar='path',
type=str,
nargs='?',
help=
'The directory in which to build libspark (defaults to [repo_root]/build)'
)
parser.add_argument(
'-t',
'--target',
type=str,
action='append',
nargs='+',
help='The target(s) to build')
parser.add_argument(
'-j',
'--jobs',
type=int,
help='[CMake] The number of parallel jobs to run')
add_toggle_opt(
parser, 'clean', help='Whether or not to perform a clean build')
parser.add_argument(
'--verbosity',
metavar='[q(uiet)|m(inimal)|n(ormal)|d(etailed)|diag(nostic)]',
choices=[
'q', 'quiet', 'm', 'minimal', 'n', 'normal', 'd', 'detailed',
'diag', 'diagnostic'
],
type=str,
help='The output verbosity level')
parser.set_defaults(
build_type='Debug', compiler='gcc', verbosity='minimal')
return parser
def _configure(src_dir, build_dir, compiler: CMakeCompilerPair):
'''Configures sparkd (server) build'''
cmd = CMakeConfigureCommand(
src_dir=src_dir,
build_dir=build_dir,
generator='"Ninja Multi-Config"',
compiler=compiler)
cmd.add_opt('BUILD_API_SERVER', True)
cmd.add_opt('BUILD_SPARKD', False)
cmd.add_opt('BUILD_WEB', False)
cmd.add_opt('CMAKE_EXPORT_COMPILE_COMMANDS', True)
return _run_cmd(str(cmd))
def _check_results(proc_results):
for res in proc_results:
if res.returncode != 0:
exit_msg(1, f'The following build subprocess failed: {res}')
def _run_cmd(cmd):
print(f'run cmd: {cmd}')
return subprocess.run(cmd, shell=True)
def _dotnet_build(build_type='Debug', verbosity='minimal'):
'''Builds dotnet'''
with (CWD(SparkPaths.root())):
cmd_parts = [f'dotnet build Spark.Api.Server.sln']
if verbosity is not None:
cmd_parts.append(f'--verbosity {verbosity}')
if build_type is not None:
cmd_parts.append(f'--configuration {build_type}')
return _run_cmd(' '.join(cmd_parts))
def _dotnet_clean(build_type='Debug', verbosity='minimal'):
'''Cleans dotnet'''
with (CWD(SparkPaths.root())):
cmd_parts = [f'dotnet clean Spark.Api.Server.sln']
if verbosity is not None:
cmd_parts.append(f'--verbosity {verbosity}')
if build_type is not None:
cmd_parts.append(f'--configuration {build_type}')
return _run_cmd(' '.join(cmd_parts))
def _cmake_build(
build_dir, build_type=None, targets=None, jobs=None, verbose=False):
'''Builds libspark'''
build_type = build_type or 'release'
build_dir = as_path(build_dir).value()
cmd_parts = [f'cmake --build {build_dir} --config {build_type.title()}']
if jobs is not None:
cmd_parts.append(f'-j {jobs}')
if not is_none_or_empty(targets):
cmd_parts.append(' '.join([f"--target {t}" for t in targets]))
if verbose:
cmd_parts.append('--verbose')
return _run_cmd(' '.join(cmd_parts))
def _cmake_clean(build_dir, build_type=None, jobs=None, verbose=False):
'''Cleans libspark'''
# In cmake, 'clean' is technically a build target
_cmake_build(build_dir, build_type, ['clean'], jobs=jobs, verbose=verbose)
def _build(
src_dir,
cmake_build_dir,
build_type: str,
compiler=None,
targets=None,
clean=False,
jobs=None,
cmake_verbose=False,
dotnet_verbosity: Optional[str] = None) -> list:
'''Builds libspark'''
# check if explicit no-op
if targets is not None and len(targets) == 0:
return []
compiler = compiler or CMakeCompilerPair('gcc', 'gcc', 'g++')
cmake_build_dir = as_path(cmake_build_dir).value()
proc_results = []
try:
create_dir(cmake_build_dir)
proc_results.append(
_configure(
src_dir=src_dir, build_dir=cmake_build_dir, compiler=compiler))
if proc_results[-1].returncode:
# Build is broken (exit early)
return proc_results
except Exception as e:
exit_msg(1, e)
if clean:
# cmake clean
proc_results.append(
_cmake_clean(cmake_build_dir, build_type, jobs, cmake_verbose))
# dotnet clean
proc_results.append(_dotnet_clean(build_type, dotnet_verbosity))
# cmake build
proc_results.append(
_cmake_build(
cmake_build_dir, build_type, targets, jobs, cmake_verbose))
return proc_results
def _main():
args = BuildApiServerArgs(_get_arg_parser().parse_args())
proc_results = _build(
src_dir=SparkPaths.root(),
cmake_build_dir=args.cmake_build_dir,
build_type=args.build_type,
compiler=args.compiler,
targets=args.targets,
jobs=args.jobs,
cmake_verbose=args.cmake_verbose,
dotnet_verbosity=args.dotnet_verbosity)
_check_results(proc_results)
if __name__ == '__main__':
_main()
+7 -236
View File
@@ -1,247 +1,18 @@
from abc import ABC, abstractmethod
import argparse
from pathlib import Path
import subprocess
from core.argparse_utils import add_toggle_opt, get_arg
from core.file_utils import SparkPaths, copy, create_dir
from core.table_formatter import fmt_table
from core.build_target import BuildTarget
from core.cmake_utils import CMakeTarget
from core.dotnet_utils import CsprojTarget
from core.file_utils import SparkPaths, create_dir
from core.logging import exit_msg
from core.iter_utils import at_bounded, join_t
from core.iter_utils import at_bounded
from core.visual_studio_utils import get_preferred_vs_context, VisualStudioContext
from typing import Optional
class VisualStudioContext:
def __init__(
self,
version,
year,
prompt_path: Path,
msbuild_path: Path,
msbuild_arg_prefix,
):
self.version = version
self.year = year
self.prompt_path = prompt_path
self.msbuild_path = msbuild_path
self._msbuild_arg_prefix = msbuild_arg_prefix
def prompt_cmd(self):
return f'{self.prompt_path} -arch=x64 -host_arch=x64'
def msbuild_flag(self, flag):
return f'{self._msbuild_arg_prefix}{flag}'
def msbuild_clp(self, key, value=None):
rv = f'{self._msbuild_arg_prefix}clp:{key}'
if value is not None:
rv = f'{rv}={value}'
return rv
def msbuild_target(self, target):
return f'{self._msbuild_arg_prefix}t:{target}'
def msbuild_prop(self, key, value):
return f'{self._msbuild_arg_prefix}p:{key}={value}'
def _get_supported_vs_contexts():
'''Gets a list of supported Visual Studio contexts (versions)'''
# vs 17
vs17_path = Path('C:/Program Files/Microsoft Visual Studio/2022/Community')
vs17 = VisualStudioContext(
17,
2022,
vs17_path.joinpath('Common7/Tools/VsDevCmd.bat'),
vs17_path.joinpath('Msbuild/Current/Bin/amd64/MSBuild.exe'),
'-',
)
# vs 16
vs16_path = Path(
'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community')
vs16 = VisualStudioContext(
16,
2019,
vs16_path.joinpath('Common7/Tools/VsDevCmd.bat'),
vs16_path.joinpath('MSBuild/Current/Bin/MSBuild.exe'),
'/',
)
return [vs17, vs16]
def _get_preferred_vs_context():
'''Gets the most preferred Visual Studio context (version) on this computer'''
vs_ctxs = _get_supported_vs_contexts()
for vs_context in vs_ctxs:
if vs_context.prompt_path.exists():
return vs_context
supported_versions = ', '.join([str(v.version) for v in vs_ctxs])
raise ValueError(
f'Failed to find supported Visual Studio version (works with {supported_versions})'
)
class BuildTarget(ABC):
def __init__(
self,
name,
source_path: Optional[Path],
out_path: Optional[Path],
aliases=[]):
self.name = name
self.source_path = source_path
self.out_path = out_path
self.aliases = aliases
@abstractmethod
def configure(self):
pass
@abstractmethod
def build(self):
pass
def is_match(self, targets):
return any(
t.lower() == self.name.lower() or t in self.aliases
for t in targets)
class CMakeTarget(BuildTarget):
def __init__(
self,
name,
preset='spark',
build_preset='spark-debug',
source_path=SparkPaths.root(),
out_path=SparkPaths.default_build(),
aliases=[],
verbosity: Optional[str] = None,
):
if out_path is None:
raise ValueError('out_path is None')
if source_path is None:
raise ValueError('source_path is None')
BuildTarget.__init__(self, name, source_path, out_path, aliases)
self.preset = preset
self.build_preset = build_preset
self.verbosity = verbosity
def _get_configure_cmd(self):
cmd_parts = ['cmake']
if self.preset is not None:
cmd_parts.append(f'--preset {self.preset}')
if self.verbosity is not None:
cmd_parts.append(f'-DCMAKE_MESSAGE_LOG_LEVEL={self.verbosity}')
if self.source_path is not None:
cmd_parts.append(f'-S {self.source_path}')
if self.out_path is not None:
cmd_parts.append(f'-B {self.out_path}')
return ' '.join(cmd_parts)
def _get_build_cmd(self):
cmd_parts = ['cmake --build']
# In CMake, the build dir arg (-B) can't be combined w/ --preset
if self.build_preset is not None:
cmd_parts.append(f'--preset {self.build_preset}')
elif self.out_path is not None:
cmd_parts.append(f'{self.out_path}')
return ' '.join(cmd_parts)
def configure(self):
cmd = self._get_configure_cmd()
print(f'configure_cmd: {cmd}')
subprocess.run(cmd).check_returncode()
if self.out_path is not None:
copy(
SparkPaths.tools().joinpath('cpp.build.props'),
self.out_path.joinpath('Directory.Build.Props'))
def build(self):
cmd = self._get_build_cmd()
print(f'build_cmd: {cmd}')
subprocess.run(cmd).check_returncode()
def __str__(self):
return fmt_table(
{
'name': self.name,
'configure_cmd': self._get_configure_cmd(),
'build_cmd': self._get_build_cmd(),
})
class CsprojTarget(BuildTarget):
def __init__(
self,
name: str,
source_path: Path,
out_path: Path,
aliases=[],
configuration: str = 'debug',
verbosity: str = 'minimal',
vs_context: Optional[VisualStudioContext] = None,
restore=True,
):
BuildTarget.__init__(self, name, source_path, out_path, aliases)
self.configuration = configuration
self.verbosity = verbosity
self.restore = restore
self.vs_context = vs_context or _get_preferred_vs_context()
def _get_build_cmd(self):
cfg = self.configuration
build_cmd_steps = [self.vs_context.prompt_cmd()]
base_output_path_arg = self.vs_context.msbuild_prop(
'BaseOutputPath', self.out_path.joinpath(cfg.title()))
if self.restore:
# Restore build (download nugets)
restore_cmd_args = [
f'"{self.vs_context.msbuild_path}"',
f"{self.vs_context.msbuild_flag('m')}",
f"{self.vs_context.msbuild_prop('platform', 'x64')}",
f"{self.vs_context.msbuild_prop('configuration', cfg.title())}",
f"{self.vs_context.msbuild_target('restore')}",
f"{self.vs_context.msbuild_clp('verbosity', self.verbosity)}",
f"{self.vs_context.msbuild_clp('summary')}",
base_output_path_arg,
self.source_path,
]
restore_cmd = join_t(' ', restore_cmd_args)
build_cmd_steps.append(restore_cmd)
# Main build
dbg_type = 'portable' if cfg.lower() == 'debug' else 'none'
build_cmd_prefix = ' '.join(
[
f'"{self.vs_context.msbuild_path}"',
f"{self.vs_context.msbuild_flag('m')}",
f"{self.vs_context.msbuild_prop('platform', 'x64')}",
f"{self.vs_context.msbuild_prop('GenerateFullPaths', 'true')}",
f"{self.vs_context.msbuild_prop('configuration', cfg.title())}",
f"{self.vs_context.msbuild_prop('debugtype', dbg_type)}",
f"{self.vs_context.msbuild_clp('verbosity', self.verbosity)}",
f"{self.vs_context.msbuild_clp('summary')}"
])
build_cmd_steps.append(
f'{build_cmd_prefix} {base_output_path_arg} {self.source_path}')
return ' && '.join(build_cmd_steps)
def configure(self):
pass
def build(self):
subprocess.run(self._get_build_cmd()).check_returncode()
def __str__(self):
return fmt_table(
{
'name': self.name,
'build_cmd': self._get_build_cmd()
})
def _get_arg_parser():
parser = argparse.ArgumentParser(description='Build spark_client.sln')
parser.add_argument(
@@ -397,7 +168,7 @@ def _main():
if tgt_names is None or len(tgt_names) == 0:
tgt_names = ['libspark', 'spark']
vs_context = _get_preferred_vs_context()
vs_context = get_preferred_vs_context()
tgts = _get_targets(
build_type=args.configuration,
build_dir=build_dir,
+1 -2
View File
@@ -2,9 +2,8 @@ import argparse
from typing import cast
import subprocess
from cmake.cmake_utils import CMakeCompilerPair, CMakeConfigureCommand
from core.argparse_utils import add_toggle_opt, get_arg
from core.cmake_utils import CMakeCompilerPair, CMakeConfigureCommand
from core.argparse_utils import add_toggle_opt, get_arg
from core.file_utils import CWD, SparkPaths, as_path, create_dir
from core.iter_utils import distinct, flatten
from core.logging import exit_msg
-57
View File
@@ -1,57 +0,0 @@
from typing import Optional
class CMakeCompilerPair:
'''Represents a pair of c, cxx compilers'''
def __init__(self, name, c, cxx):
self.name = name
self.c = c
self.cxx = cxx
def to_opts(self):
opts = {}
if self.c:
opts['C_COMPILER'] = self.c
if self.cxx:
opts['CXX_COMPILER'] = self.cxx
return opts
def __str__(self):
return f'{self.name} ({self.c}, {self.cxx})'
class CMakeConfigureCommand:
def __init__(
self,
src_dir,
build_dir,
generator: Optional[str] = None,
build_type: Optional[str] = None,
compiler: Optional[CMakeCompilerPair] = None):
if src_dir is None:
raise ValueError('Source dir cannot be None')
self.cmd = 'cmake'
self.add_opt('-S', src_dir)
self.add_opt('-B', build_dir)
if generator is not None:
self.add_opt('-G', generator)
if build_type is not None:
self.add_opt('CMAKE_BUILD_TYPE', build_type)
if compiler is not None:
self.add_opt('CMAKE_C_COMPILER', compiler.c)
self.add_opt('CMAKE_CXX_COMPILER', compiler.cxx)
def add_opt(self, name: str, value):
if name is None:
raise ValueError('opt name cannot be None')
if value is None:
return self
if not name.startswith('-'):
name = f'-D{name}'
self.cmd = f'{self.cmd} {name}={value}'
else:
self.cmd = f'{self.cmd} {name} {value}'
return self
def __str__(self):
return self.cmd
+35
View File
@@ -0,0 +1,35 @@
from abc import ABC, abstractmethod
from pathlib import Path
import subprocess
from core.table_formatter import fmt_table
from core.iter_utils import join_t
from core.visual_studio_utils import get_preferred_vs_context, VisualStudioContext
from typing import Optional
class BuildTarget(ABC):
def __init__(
self,
name,
source_path: Optional[Path],
out_path: Optional[Path],
aliases=[]):
self.name = name
self.source_path = source_path
self.out_path = out_path
self.aliases = aliases
@abstractmethod
def configure(self):
pass
@abstractmethod
def build(self):
pass
def is_match(self, targets):
return any(
t.lower() == self.name.lower() or t in self.aliases
for t in targets)
+85
View File
@@ -1,4 +1,9 @@
import subprocess
from build_target import BuildTarget
from os import PathLike
from file_utils import copy, SparkPaths
from table_formatter import fmt_table
from typing import Optional
@@ -63,3 +68,83 @@ class CMakeConfigureCommand:
def __str__(self):
return self.cmd
class CMakeTarget(BuildTarget):
def __init__(
self,
name,
preset='spark',
build_preset='spark-debug',
source_path=SparkPaths.root(),
out_path=SparkPaths.default_build(),
aliases=[],
verbosity: Optional[str] = None,
):
if out_path is None:
raise ValueError('out_path is None')
if source_path is None:
raise ValueError('source_path is None')
BuildTarget.__init__(self, name, source_path, out_path, aliases)
self.preset = preset
self.build_preset = build_preset
self.verbosity = verbosity
def _get_configure_cmd(self):
cmd_parts = ['cmake']
if self.preset is not None:
cmd_parts.append(f'--preset {self.preset}')
if self.verbosity is not None:
cmd_parts.append(f'-DCMAKE_MESSAGE_LOG_LEVEL={self.verbosity}')
if self.source_path is not None:
cmd_parts.append(f'-S {self.source_path}')
if self.out_path is not None:
cmd_parts.append(f'-B {self.out_path}')
return ' '.join(cmd_parts)
def _get_build_cmd(self):
cmd_parts = ['cmake --build']
# In CMake, the build dir arg (-B) can't be combined w/ --preset
if self.build_preset is not None:
cmd_parts.append(f'--preset {self.build_preset}')
elif self.out_path is not None:
cmd_parts.append(f'{self.out_path}')
return ' '.join(cmd_parts)
def configure(self):
cmd = self._get_configure_cmd()
print(f'configure_cmd: {cmd}')
subprocess.run(cmd).check_returncode()
if self.out_path is not None:
copy(
SparkPaths.tools().joinpath('cpp.build.props'),
self.out_path.joinpath('Directory.Build.Props'))
def build(self):
cmd = self._get_build_cmd()
print(f'build_cmd: {cmd}')
subprocess.run(cmd).check_returncode()
def __str__(self):
return fmt_table(
{
'name': self.name,
'configure_cmd': self._get_configure_cmd(),
'build_cmd': self._get_build_cmd(),
})
def get_cmake_build_type(build_type: str):
'''
Converts a case-insensitive build-type str (from cli) to a case-sensitive CMAKE_BUILD_TYPE var
'''
# The 'build_type' arg is case-sensitive because 'CMAKE_BUILD_TYPE' is substitued for a folder
# in the externals-libs path. In order to match a real path, it must be 'Debug' or 'Release'.
# Keep the orig_build_type around for potential use in err msg.
orig_build_type = build_type
build_type = build_type or 'debug'
build_type = build_type.title()
if build_type not in ['Debug', 'Release']:
raise ValueError(f"Unexpected build_type '{orig_build_type}'")
return build_type
+78
View File
@@ -0,0 +1,78 @@
from pathlib import Path
import subprocess
from core.table_formatter import fmt_table
from core.build_target import BuildTarget
from core.iter_utils import join_t
from core.visual_studio_utils import get_preferred_vs_context, VisualStudioContext
from typing import Optional
class CsprojTarget(BuildTarget):
def __init__(
self,
name: str,
source_path: Path,
out_path: Path,
aliases=[],
configuration: str = 'debug',
verbosity: str = 'minimal',
vs_context: Optional[VisualStudioContext] = None,
restore=True,
):
BuildTarget.__init__(self, name, source_path, out_path, aliases)
self.configuration = configuration
self.verbosity = verbosity
self.restore = restore
self.vs_context = vs_context or get_preferred_vs_context()
def _get_build_cmd(self):
cfg = self.configuration
build_cmd_steps = [self.vs_context.prompt_cmd()]
base_output_path_arg = self.vs_context.msbuild_prop(
'BaseOutputPath', self.out_path.joinpath(cfg.title()))
if self.restore:
# Restore build (download nugets)
restore_cmd_args = [
f'"{self.vs_context.msbuild_path}"',
f"{self.vs_context.msbuild_flag('m')}",
f"{self.vs_context.msbuild_prop('platform', 'x64')}",
f"{self.vs_context.msbuild_prop('configuration', cfg.title())}",
f"{self.vs_context.msbuild_target('restore')}",
f"{self.vs_context.msbuild_clp('verbosity', self.verbosity)}",
f"{self.vs_context.msbuild_clp('summary')}",
base_output_path_arg,
self.source_path,
]
restore_cmd = join_t(' ', restore_cmd_args)
build_cmd_steps.append(restore_cmd)
# Main build
dbg_type = 'portable' if cfg.lower() == 'debug' else 'none'
build_cmd_prefix = ' '.join(
[
f'"{self.vs_context.msbuild_path}"',
f"{self.vs_context.msbuild_flag('m')}",
f"{self.vs_context.msbuild_prop('platform', 'x64')}",
f"{self.vs_context.msbuild_prop('GenerateFullPaths', 'true')}",
f"{self.vs_context.msbuild_prop('configuration', cfg.title())}",
f"{self.vs_context.msbuild_prop('debugtype', dbg_type)}",
f"{self.vs_context.msbuild_clp('verbosity', self.verbosity)}",
f"{self.vs_context.msbuild_clp('summary')}"
])
build_cmd_steps.append(
f'{build_cmd_prefix} {base_output_path_arg} {self.source_path}')
return ' && '.join(build_cmd_steps)
def configure(self):
pass
def build(self):
subprocess.run(self._get_build_cmd()).check_returncode()
def __str__(self):
return fmt_table(
{
'name': self.name,
'build_cmd': self._get_build_cmd()
})
+72
View File
@@ -0,0 +1,72 @@
from pathlib import Path
class VisualStudioContext:
def __init__(
self,
version,
year,
prompt_path: Path,
msbuild_path: Path,
msbuild_arg_prefix,
):
self.version = version
self.year = year
self.prompt_path = prompt_path
self.msbuild_path = msbuild_path
self._msbuild_arg_prefix = msbuild_arg_prefix
def prompt_cmd(self):
return f'{self.prompt_path} -arch=x64 -host_arch=x64'
def msbuild_flag(self, flag):
return f'{self._msbuild_arg_prefix}{flag}'
def msbuild_clp(self, key, value=None):
rv = f'{self._msbuild_arg_prefix}clp:{key}'
if value is not None:
rv = f'{rv}={value}'
return rv
def msbuild_target(self, target):
return f'{self._msbuild_arg_prefix}t:{target}'
def msbuild_prop(self, key, value):
return f'{self._msbuild_arg_prefix}p:{key}={value}'
def get_supported_vs_contexts():
'''Gets a list of supported Visual Studio contexts (versions)'''
# vs 17
vs17_path = Path('C:/Program Files/Microsoft Visual Studio/2022/Community')
vs17 = VisualStudioContext(
17,
2022,
vs17_path.joinpath('Common7/Tools/VsDevCmd.bat'),
vs17_path.joinpath('Msbuild/Current/Bin/amd64/MSBuild.exe'),
'-',
)
# vs 16
vs16_path = Path(
'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community')
vs16 = VisualStudioContext(
16,
2019,
vs16_path.joinpath('Common7/Tools/VsDevCmd.bat'),
vs16_path.joinpath('MSBuild/Current/Bin/MSBuild.exe'),
'/',
)
return [vs17, vs16]
def get_preferred_vs_context():
'''Gets the most preferred Visual Studio context (version) on this computer'''
vs_ctxs = get_supported_vs_contexts()
for vs_context in vs_ctxs:
if vs_context.prompt_path.exists():
return vs_context
supported_versions = ', '.join([str(v.version) for v in vs_ctxs])
raise ValueError(
f'Failed to find supported Visual Studio version (works with {supported_versions})'
)