portal_tool 0.0.20.dev2__tar.gz → 0.0.20.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/PKG-INFO +2 -1
  2. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/pyproject.toml +3 -2
  3. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurators/configurator.py +4 -9
  4. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurators/linux_configurator.py +33 -52
  5. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurators/mac_configurator.py +2 -2
  6. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurators/windows_configurator.py +2 -28
  7. portal_tool-0.0.20.dev7/src/portal_tool/installer/repo/repo_maker.py +209 -0
  8. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/cookiecutter.json +4 -0
  9. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/.clang-format +56 -0
  10. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/.gitignore +134 -0
  11. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/CMakeLists.txt +12 -0
  12. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/resources/.gitkeep +0 -0
  13. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/settings.json +23 -0
  14. portal_tool-0.0.20.dev7/src/portal_tool/templates/repo/bootstrap/{{ cookiecutter.project_slug }}/source/main.cpp +56 -0
  15. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/tool.py +20 -1
  16. portal_tool-0.0.20.dev2/src/portal_tool/installer/repo/repo_maker.py +0 -130
  17. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/LICENSE +0 -0
  18. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/README.md +0 -0
  19. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/__init__.py +0 -0
  20. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/__main__.py +0 -0
  21. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/git_manager.py +0 -0
  22. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/__init__.py +0 -0
  23. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurator_factory.py +0 -0
  24. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/configurators/__init__.py +0 -0
  25. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/installer.py +0 -0
  26. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/installer/repo/build_models.py +0 -0
  27. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/models.py +0 -0
  28. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/singleton.py +0 -0
  29. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/templates/vcpkg/portfile.cmake-features.j2 +0 -0
  30. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/templates/vcpkg/portfile.cmake.j2 +0 -0
  31. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/templates/vcpkg/usage.j2 +0 -0
  32. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/templates/vcpkg/vcpkg-configuration.json.j2 +0 -0
  33. {portal_tool-0.0.20.dev2 → portal_tool-0.0.20.dev7}/src/portal_tool/vcpkg_port.py +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portal_tool
3
- Version: 0.0.20.dev2
3
+ Version: 0.0.20.dev7
4
4
  Summary: Assortment of tools for the Portal project
5
5
  Author: Jonatan Nevo
6
6
  Author-email: Jonatan Nevo <jonatannevo-git@proton.me>
7
7
  License-File: LICENSE
8
8
  Requires-Dist: appdirs>=1.4.4
9
+ Requires-Dist: cookiecutter>=2.6.0
9
10
  Requires-Dist: jinja2>=3.1.6,<4
10
11
  Requires-Dist: pathlib>=1.0.1
11
12
  Requires-Dist: pydantic>=2.11.4,<3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "portal_tool"
3
- version = "0.0.20.dev2"
3
+ version = "0.0.20.dev7"
4
4
  description = "Assortment of tools for the Portal project"
5
5
  authors = [{ name = "Jonatan Nevo", email = "jonatannevo-git@proton.me" }]
6
6
  requires-python = ">=3.12,<3.15"
@@ -9,6 +9,7 @@ license-files = ["LICENSE"]
9
9
  homepage = "https://github.com/JonatanNevo/portal-tools"
10
10
  dependencies = [
11
11
  "appdirs>=1.4.4",
12
+ "cookiecutter>=2.6.0",
12
13
  "jinja2>=3.1.6,<4",
13
14
  "pathlib>=1.0.1",
14
15
  "pydantic>=2.11.4,<3",
@@ -40,7 +41,7 @@ lint = [
40
41
  include = ["portal_tool"]
41
42
 
42
43
  [tool.hatch.build.targets.sdist.force-include]
43
- "portal_tool/templates/*.j2" = "portal_tool/templates/*.j2"
44
+ "portal_tool/templates/*" = "portal_tool/templates/*"
44
45
 
45
46
  [tool.hatch.build.targets.wheel]
46
47
  include = ["portal_tool"]
@@ -26,7 +26,7 @@ class Configurator(metaclass=abc.ABCMeta):
26
26
  if (
27
27
  installed_vcpkg.exists()
28
28
  and (
29
- installed_vcpkg / f"vcpkg{self._get_executable_extension()}"
29
+ installed_vcpkg / f"vcpkg{self.get_executable_extension()}"
30
30
  ).exists()
31
31
  ):
32
32
  return installed_vcpkg, False
@@ -37,7 +37,6 @@ class Configurator(metaclass=abc.ABCMeta):
37
37
  self._validate_compilers()
38
38
  self._validate_cmake()
39
39
  self._validate_dependencies()
40
- self._validate_uv()
41
40
 
42
41
  def _try_install_vcpkg(self) -> None:
43
42
  install_vcpkg = typer.confirm("Would you like to install vcpkg?")
@@ -68,7 +67,7 @@ class Configurator(metaclass=abc.ABCMeta):
68
67
  typer.echo("Bootstrap vcpkg...")
69
68
  subprocess.check_output(
70
69
  shlex.split(
71
- f"{installation_directory}/bootstrap-vcpkg.{self._get_script_extension()}"
70
+ f"{installation_directory}/bootstrap-vcpkg.{self.get_script_extension()}"
72
71
  )
73
72
  )
74
73
  typer.echo(f"Vcpkg installed successfully to: {installation_directory}")
@@ -85,11 +84,11 @@ class Configurator(metaclass=abc.ABCMeta):
85
84
  pass
86
85
 
87
86
  @abc.abstractmethod
88
- def _get_script_extension(self) -> str:
87
+ def get_script_extension(self) -> str:
89
88
  pass
90
89
 
91
90
  @abc.abstractmethod
92
- def _get_executable_extension(self) -> str:
91
+ def get_executable_extension(self) -> str:
93
92
  pass
94
93
 
95
94
  @abc.abstractmethod
@@ -99,7 +98,3 @@ class Configurator(metaclass=abc.ABCMeta):
99
98
  @abc.abstractmethod
100
99
  def _validate_compilers(self) -> None:
101
100
  pass
102
-
103
- @abc.abstractmethod
104
- def _validate_uv(self) -> None:
105
- pass
@@ -112,43 +112,43 @@ class LinuxConfigurator(Configurator):
112
112
  except (subprocess.SubprocessError, FileNotFoundError):
113
113
  typer.echo("Clang not found")
114
114
 
115
- # Check for gcc 15+
116
- try:
117
- result = subprocess.run(
118
- ["gcc", "--version"], capture_output=True, text=True, timeout=5
119
- )
120
- if result.returncode == 0:
121
- # Parse version from output like "gcc (GCC) X.Y.Z" or "gcc version X.Y.Z"
122
- match = re.search(r"gcc.*?(\d+)\.(\d+)", result.stdout, re.IGNORECASE)
123
- if match:
124
- major = int(match.group(1))
125
- if major >= 15:
126
- # Try to get installation path
127
- path_result = subprocess.run(
128
- ["which", "gcc"], capture_output=True, text=True, timeout=5
129
- )
130
- install_path = (
131
- path_result.stdout.strip()
132
- if path_result.returncode == 0
133
- else "unknown"
134
- )
135
- typer.echo(
136
- f"gcc {major}.{match.group(2)} found ({install_path})"
137
- )
138
- gcc_valid = True
139
- else:
140
- typer.echo(
141
- f"gcc {major}.{match.group(2)} found, but version 15+ is required"
142
- )
143
- except (subprocess.SubprocessError, FileNotFoundError):
144
- typer.echo("gcc not found")
115
+ # # Check for gcc 15+
116
+ # try:
117
+ # result = subprocess.run(
118
+ # ["gcc", "--version"], capture_output=True, text=True, timeout=5
119
+ # )
120
+ # if result.returncode == 0:
121
+ # # Parse version from output like "gcc (GCC) X.Y.Z" or "gcc version X.Y.Z"
122
+ # match = re.search(r"gcc.*?(\d+)\.(\d+)", result.stdout, re.IGNORECASE)
123
+ # if match:
124
+ # major = int(match.group(1))
125
+ # if major >= 15:
126
+ # # Try to get installation path
127
+ # path_result = subprocess.run(
128
+ # ["which", "gcc"], capture_output=True, text=True, timeout=5
129
+ # )
130
+ # install_path = (
131
+ # path_result.stdout.strip()
132
+ # if path_result.returncode == 0
133
+ # else "unknown"
134
+ # )
135
+ # typer.echo(
136
+ # f"gcc {major}.{match.group(2)} found ({install_path})"
137
+ # )
138
+ # gcc_valid = True
139
+ # else:
140
+ # typer.echo(
141
+ # f"gcc {major}.{match.group(2)} found, but version 15+ is required"
142
+ # )
143
+ # except (subprocess.SubprocessError, FileNotFoundError):
144
+ # typer.echo("gcc not found")
145
145
 
146
146
  # Require at least one valid compiler
147
147
  if not clang_valid and not gcc_valid:
148
148
  typer.echo("\nNo valid compiler found!")
149
149
  typer.echo("Please install at least one of the following:")
150
150
  typer.echo(" - Clang 19 or later")
151
- typer.echo(" - gcc 15 or later")
151
+ # typer.echo(" - gcc 15 or later")
152
152
  raise typer.Abort("Compiler validation failed")
153
153
 
154
154
  typer.echo("Compiler validation successful!")
@@ -195,27 +195,8 @@ class LinuxConfigurator(Configurator):
195
195
  "Please install them manually before building, exit now if some of them are missing"
196
196
  )
197
197
 
198
- def _validate_uv(self) -> None:
199
- proceed = False
200
- try:
201
- subprocess.check_output(["uv", "--version"])
202
- typer.echo("UV found, skipping installation")
203
- proceed = False
204
- except (subprocess.SubprocessError, FileNotFoundError):
205
- proceed = typer.confirm(
206
- "UV not found, would you like to install it?", abort=True
207
- )
208
-
209
- if proceed:
210
- typer.echo("Installing UV...")
211
- subprocess.check_output(
212
- "curl -LsSf https://astral.sh/uv/install.sh | sh",
213
- shell=True,
214
- )
215
- typer.echo("UV installation successful")
216
-
217
- def _get_script_extension(self) -> str:
198
+ def get_script_extension(self) -> str:
218
199
  return "sh"
219
200
 
220
- def _get_executable_extension(self) -> str:
201
+ def get_executable_extension(self) -> str:
221
202
  return ""
@@ -18,10 +18,10 @@ class MacConfigurator(Configurator):
18
18
  def _validate_compilers(self) -> None:
19
19
  typer.echo("Missing compiler validation, skipping...")
20
20
 
21
- def _get_script_extension(self) -> str:
21
+ def get_script_extension(self) -> str:
22
22
  return "sh"
23
23
 
24
- def _get_executable_extension(self) -> str:
24
+ def get_executable_extension(self) -> str:
25
25
  return ""
26
26
 
27
27
  def _validate_dependencies(self) -> None:
@@ -94,36 +94,10 @@ class WindowsConfigurator(Configurator):
94
94
 
95
95
  typer.echo("Compiler validation successful!")
96
96
 
97
- def _validate_uv(self) -> None:
98
- # Check for a valid uv version
99
- proceed = False
100
- try:
101
- subprocess.check_output(["uv", "--version"])
102
- typer.echo("UV found, skipping installation")
103
- proceed = False
104
- except (subprocess.SubprocessError, FileNotFoundError):
105
- proceed = typer.confirm(
106
- "UV not found, would you like to install it?", abort=True
107
- )
108
-
109
- if proceed:
110
- typer.echo("Installing UV...")
111
- subprocess.run(
112
- [
113
- "powershell",
114
- "-ExecutionPolicy",
115
- "ByPass",
116
- "-c",
117
- "irm https://astral.sh/uv/install.ps1 | iex",
118
- ],
119
- check=True,
120
- )
121
- typer.echo("UV installation successful")
122
-
123
- def _get_script_extension(self) -> str:
97
+ def get_script_extension(self) -> str:
124
98
  return "bat"
125
99
 
126
- def _get_executable_extension(self) -> str:
100
+ def get_executable_extension(self) -> str:
127
101
  return ".exe"
128
102
 
129
103
  def _validate_dependencies(self) -> None:
@@ -0,0 +1,209 @@
1
+ import importlib
2
+ import importlib.util
3
+ import json
4
+ import os
5
+ import pathlib
6
+ import platform
7
+ import shlex
8
+ import shutil
9
+ import subprocess
10
+ import zipimport
11
+
12
+ import typer
13
+ from cookiecutter.main import cookiecutter
14
+
15
+ from portal_tool.installer.configurator_factory import ConfiguratorFactory
16
+ from portal_tool.installer.repo.build_models import (
17
+ CMakePresets,
18
+ ConfigurePreset,
19
+ BuildPreset,
20
+ )
21
+
22
+
23
+ class RepoMaker:
24
+ """
25
+ A repo consists of:
26
+ - vcpkg (either submodule or globally installed)
27
+ - src
28
+ - resources
29
+ - vcpkg configuration
30
+ - cmake configuration
31
+ """
32
+
33
+ def __init__(self, path: pathlib.Path):
34
+ self.configurator = ConfiguratorFactory().create(False)
35
+ self.presets = CMakePresets()
36
+
37
+ self.name = typer.prompt("Project Name")
38
+ self.base_path = pathlib.Path(
39
+ typer.prompt(f"Base Location (will create a folder named {self.name.lower().replace(' ', '_')})",
40
+ default=path))
41
+ self.project_path = self.base_path / self.name.lower().replace(' ', '_')
42
+
43
+ self.vcpkg_toolchain_location = "{}/scripts/buildsystems/vcpkg.cmake"
44
+ self.use_global = False
45
+ self.vcpkg_exec_location = pathlib.Path("")
46
+
47
+ self._create_repo_from_template()
48
+ self._configure_git()
49
+ self._setup_vcpkg()
50
+ self._configure_build_system()
51
+
52
+ def _find_pacakge_path(self) -> pathlib.Path:
53
+ source_path = "templates/repo/bootstrap"
54
+ spec = importlib.util.find_spec("portal_tool")
55
+ assert spec is not None, "An import spec was not found for the package."
56
+ loader = spec.loader
57
+ assert loader is not None, "A loader was not found for the package."
58
+
59
+ if isinstance(loader, zipimport.zipimporter):
60
+ self._archive = loader.archive
61
+ pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
62
+ template_root = os.path.join(pkgdir, source_path).rstrip(os.path.sep)
63
+ else:
64
+ roots: list[str] = []
65
+
66
+ # One element for regular packages, multiple for namespace
67
+ # packages, or None for single module file.
68
+ if spec.submodule_search_locations:
69
+ roots.extend(spec.submodule_search_locations)
70
+ # A single module file, use the parent directory instead.
71
+ elif spec.origin is not None:
72
+ roots.append(os.path.dirname(spec.origin))
73
+
74
+ if not roots:
75
+ raise ValueError(
76
+ f"The portal_tool package was not installed in a"
77
+ " way that PackageLoader understands."
78
+ )
79
+
80
+ for root in roots:
81
+ root = os.path.join(root, source_path)
82
+
83
+ if os.path.isdir(root):
84
+ template_root = root
85
+ break
86
+ else:
87
+ raise ValueError(
88
+ f"PackageLoader could not find a {source_path!r} directory"
89
+ f" in the portal_tool package."
90
+ )
91
+ return pathlib.Path(template_root)
92
+
93
+ def _create_repo_from_template(self) -> None:
94
+ if self.project_path.exists():
95
+ proceed = typer.confirm(
96
+ "Found existing project, would you like to continue? (delete folder)"
97
+ )
98
+ if not proceed:
99
+ raise typer.Abort("Aborting repo creation.")
100
+ return
101
+
102
+ cookiecutter(
103
+ self._find_pacakge_path().as_posix(),
104
+ no_input=True,
105
+ extra_context={"project_name": self.name},
106
+ output_dir=self.base_path.as_posix(),
107
+ )
108
+
109
+ def _configure_git(self) -> None:
110
+ typer.echo(f"Initializing git repo in: {self.project_path}")
111
+ subprocess.check_output(shlex.split(f'git -C "{self.project_path}" init'))
112
+
113
+ def _setup_vcpkg(self) -> None:
114
+ vcpkg_root, found_using_env = self.configurator.find_vcpkg_root()
115
+
116
+ self.use_global = False
117
+ if vcpkg_root:
118
+ self.use_global = typer.confirm(
119
+ f"Found global vcpkg, [{vcpkg_root}] would you like to use? (if not, a local submodule will be created)"
120
+ )
121
+
122
+ if not self.use_global:
123
+ typer.echo(f"Creating vcpkg submodule in: {self.project_path / 'vcpkg'}")
124
+ try:
125
+ subprocess.check_output(
126
+ shlex.split(
127
+ f'git -C "{self.project_path}" submodule add https://github.com/microsoft/vcpkg "{"vcpkg"}"'
128
+ )
129
+ )
130
+ subprocess.check_output(f'{self.project_path.as_posix()}/vcpkg/bootstrap-vcpkg.{self.configurator.get_script_extension()}', shell=True)
131
+ self.vcpkg_exec_location = self.project_path / "vcpkg" / f"vcpkg{self.configurator.get_executable_extension()}"
132
+ except subprocess.CalledProcessError:
133
+ typer.echo("Failed to add submodule, please add it manually.")
134
+ else:
135
+ self.vcpkg_exec_location = vcpkg_root / f"vcpkg{self.configurator.get_executable_extension()}"
136
+
137
+ self.vcpkg_toolchain_location = self.vcpkg_toolchain_location.format(
138
+ (
139
+ "$env{VCPKG_ROOT}"
140
+ if found_using_env
141
+ else (pathlib.Path(os.path.expanduser("~")) / ".vcpkg").as_posix()
142
+ )
143
+ if self.use_global
144
+ else "${sourceDir}/vcpkg"
145
+ )
146
+
147
+ def _configure_build_system(self) -> None:
148
+ base = ConfigurePreset(
149
+ name="base",
150
+ hidden=True,
151
+ binaryDir="${sourceDir}/build/${presetName}",
152
+ cacheVariables={
153
+ "CMAKE_TOOLCHAIN_FILE": self.vcpkg_toolchain_location,
154
+ "CMAKE_CONFIGURATION_TYPES": "Debug;RelWithDebInfo;Release",
155
+ },
156
+ )
157
+
158
+ # TODO: determine generator (ninja-multi, xcode, vs)
159
+ ninja_multi = ConfigurePreset(
160
+ name="ninja-multi", inherits=[base.name], generator="Ninja Multi-Config"
161
+ )
162
+
163
+ # TODO: support clang
164
+ if platform.system() == "Linux":
165
+ ninja_multi.environment = {
166
+ "CC": "clang",
167
+ "CXX": "clang++",
168
+ "VCPKG_KEEP_ENV_VARS": "CC;CXX",
169
+ **(ninja_multi.environment if ninja_multi.environment else {}),
170
+ }
171
+
172
+ self.presets.configure_presets = [base, ninja_multi]
173
+
174
+ self.presets.build_presets = [
175
+ BuildPreset(
176
+ name="debug", configurePreset=ninja_multi.name, configuration="Debug"
177
+ ),
178
+ BuildPreset(
179
+ name="development",
180
+ configurePreset=ninja_multi.name,
181
+ configuration="RelWithDebInfo",
182
+ ),
183
+ BuildPreset(
184
+ name="release",
185
+ configurePreset=ninja_multi.name,
186
+ configuration="Release",
187
+ ),
188
+ ]
189
+
190
+ (self.project_path / "CMakePresets.json").write_text(
191
+ self.presets.model_dump_json(indent=4, exclude_none=True)
192
+ )
193
+
194
+ vcpkg_config = {
195
+ "registries": [
196
+ {
197
+ "kind": "git",
198
+ "repository": "https://github.com/JonatanNevo/portal-vcpkg-registry",
199
+ "reference": "main",
200
+ "baseline": "07f01155c58797765efee4f55a4acaf621cee5da",
201
+ "packages": ["portal-core", "portal-application", "portal-input", "portal-networking",
202
+ "portal-serialization", "portal-engine", "enchantum", "spdlog", "glaze", "llvm-adt"]
203
+ }
204
+ ]
205
+ }
206
+
207
+ (self.project_path / "vcpkg_configuration.json").write_text(json.dumps(vcpkg_config, indent=4))
208
+
209
+ subprocess.check_output(shlex.split(f"{self.vcpkg_exec_location.as_posix()} x-update-baseline"))
@@ -0,0 +1,4 @@
1
+ {
2
+ "project_name": "Portal Bootstrap Game",
3
+ "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_') }}"
4
+ }
@@ -0,0 +1,56 @@
1
+ ---
2
+ Language: Cpp
3
+ BasedOnStyle: LLVM
4
+ AccessModifierOffset: -4
5
+ AlignAfterOpenBracket: AlwaysBreak
6
+ AlignConsecutiveAssignments: false
7
+ AlignConsecutiveDeclarations: false
8
+ AlignOperands: false
9
+ AlignTrailingComments: true
10
+ AlwaysBreakTemplateDeclarations: Yes
11
+ BinPackArguments: false
12
+ BinPackParameters: false
13
+ PackConstructorInitializers: NextLineOnly
14
+ BraceWrapping:
15
+ AfterCaseLabel: true
16
+ AfterClass: true
17
+ AfterControlStatement: true
18
+ AfterEnum: true
19
+ AfterFunction: true
20
+ AfterNamespace: true
21
+ AfterStruct: true
22
+ AfterUnion: true
23
+ AfterExternBlock: true
24
+ BeforeCatch: true
25
+ BeforeElse: true
26
+ BeforeLambdaBody: true
27
+ BeforeWhile: true
28
+ SplitEmptyFunction: false
29
+ SplitEmptyRecord: false
30
+ SplitEmptyNamespace: false
31
+ BreakBeforeBraces: Custom
32
+ BreakConstructorInitializers: AfterColon
33
+ ColumnLimit: 150
34
+ IncludeCategories:
35
+ - Regex: '^<.*'
36
+ Priority: 1
37
+ - Regex: '^".*'
38
+ Priority: 2
39
+ - Regex: '.*'
40
+ Priority: 3
41
+ IncludeIsMainRegex: '([-_](test|unittest))?$'
42
+ IndentCaseBlocks: true
43
+ IndentWidth: 4
44
+ InsertNewlineAtEOF: true
45
+ MacroBlockBegin: ''
46
+ MacroBlockEnd: ''
47
+ MaxEmptyLinesToKeep: 2
48
+ NamespaceIndentation: Inner
49
+ PointerAlignment: Left
50
+ SpaceInEmptyParentheses: false
51
+ SpacesInAngles: false
52
+ SpacesInConditionalStatement: false
53
+ SpacesInCStyleCastParentheses: false
54
+ SpacesInParentheses: false
55
+ TabWidth: 4
56
+ ...
@@ -0,0 +1,134 @@
1
+ ### C++ template
2
+ # Prerequisites
3
+ *.d
4
+
5
+ # Compiled Object files
6
+ *.slo
7
+ *.lo
8
+ *.o
9
+ *.obj
10
+
11
+ # Precompiled Headers
12
+ *.gch
13
+ *.pch
14
+
15
+ # Compiled Dynamic libraries
16
+ *.so
17
+ *.dylib
18
+ *.dll
19
+
20
+ # Fortran module files
21
+ *.mod
22
+ *.smod
23
+
24
+ # Compiled Static libraries
25
+ *.lai
26
+ *.la
27
+ *.a
28
+ *.lib
29
+
30
+ # Executables
31
+ *.exe
32
+ *.out
33
+ *.app
34
+
35
+ ### vcpkg template
36
+ # Vcpkg
37
+
38
+ vcpkg-manifest-install.log
39
+
40
+ ## Manifest Mode
41
+ vcpkg_installed/
42
+
43
+ ### CLion template
44
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
45
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
46
+
47
+ # User-specific stuff
48
+ .idea/**/workspace.xml
49
+ .idea/**/tasks.xml
50
+ .idea/**/usage.statistics.xml
51
+ .idea/**/dictionaries
52
+ .idea/**/shelf
53
+
54
+ # AWS User-specific
55
+ .idea/**/aws.xml
56
+
57
+ # Generated files
58
+ .idea/**/contentModel.xml
59
+
60
+ # Sensitive or high-churn files
61
+ .idea/**/dataSources/
62
+ .idea/**/dataSources.ids
63
+ .idea/**/dataSources.local.xml
64
+ .idea/**/sqlDataSources.xml
65
+ .idea/**/dynamic.xml
66
+ .idea/**/uiDesigner.xml
67
+ .idea/**/dbnavigator.xml
68
+
69
+ # Gradle
70
+ .idea/**/gradle.xml
71
+ .idea/**/libraries
72
+
73
+ # Gradle and Maven with auto-import
74
+ # When using Gradle or Maven with auto-import, you should exclude module files,
75
+ # since they will be recreated, and may cause churn. Uncomment if using
76
+ # auto-import.
77
+ # .idea/artifacts
78
+ # .idea/compiler.xml
79
+ # .idea/jarRepositories.xml
80
+ # .idea/modules.xml
81
+ # .idea/*.iml
82
+ # .idea/modules
83
+ # *.iml
84
+ # *.ipr
85
+
86
+ # CMake
87
+ cmake-build-*/
88
+ CMakeLists.txt.user
89
+ CMakeCache.txt
90
+ CMakeFiles
91
+ CMakeScripts
92
+ Testing
93
+ Makefile
94
+ cmake_install.cmake
95
+ install_manifest.txt
96
+ compile_commands.json
97
+ CTestTestfile.cmake
98
+ _deps
99
+ CMakeUserPresets.json
100
+
101
+ # Mongo Explorer plugin
102
+ .idea/**/mongoSettings.xml
103
+
104
+ # File-based project format
105
+ *.iws
106
+
107
+ # IntelliJ
108
+ out/
109
+
110
+ # mpeltonen/sbt-idea plugin
111
+ .idea_modules/
112
+
113
+ # JIRA plugin
114
+ atlassian-ide-plugin.xml
115
+
116
+ # Cursive Clojure plugin
117
+ .idea/replstate.xml
118
+
119
+ # SonarLint plugin
120
+ .idea/sonarlint/
121
+
122
+ # Crashlytics plugin (for Android Studio and IntelliJ)
123
+ com_crashlytics_export_strings.xml
124
+ crashlytics.properties
125
+ crashlytics-build.properties
126
+ fabric.properties
127
+
128
+ # Editor-based Rest Client
129
+ .idea/httpRequests
130
+
131
+ # Android studio 3.1+ serialized cache file
132
+ .idea/caches/build_file_checksums.ser
133
+
134
+ build*/
@@ -0,0 +1,12 @@
1
+ cmake_minimum_required(VERSION 3.30)
2
+ project({{ cookiecutter.project_name }})
3
+
4
+ #find_package(portal-engine CONFIG REQUIRED)
5
+
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ file(GLOB_RECURSE SOURCES source/*.cpp)
9
+
10
+ portal_add_game({{ cookiecutter.project_slug }}
11
+ SOURCES ${SOURCES}
12
+ )
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{ cookiecutter.project_name }}",
3
+
4
+ "application": {
5
+ "fullscreen": false,
6
+ "scheduler-threads": 0,
7
+ "window": {
8
+ "height": 900,
9
+ "width": 1600
10
+ }
11
+ },
12
+
13
+ "engine": {
14
+ "include_engine_resources": true,
15
+ "resources": [
16
+ {
17
+ "path": "resources",
18
+ "type": "Folder"
19
+ }
20
+ ]
21
+ },
22
+ "log-level": "Debug"
23
+ }
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright © 2025 Jonatan Nevo.
3
+ // Distributed under the MIT license (see LICENSE file).
4
+ //
5
+
6
+ #include <portal/engine/settings.h>
7
+ #include <portal/engine/engine.h>
8
+
9
+ #include <portal/application/application.h>
10
+ #include <portal/application/entry_point.h>
11
+
12
+ using namespace portal;
13
+
14
+ constexpr auto LOG_LEVEL_ENTRY = "log-level";
15
+
16
+ void initialize_settings()
17
+ {
18
+ Settings::init(SettingsArchiveType::Json, "settings.json");
19
+ }
20
+
21
+ void initialize_logger()
22
+ {
23
+ auto log_level_string = Settings::get().get_setting<std::string>(LOG_LEVEL_ENTRY);
24
+ if (log_level_string)
25
+ {
26
+ const auto log_level = portal::from_string<Log::LogLevel>(*log_level_string);
27
+ Log::set_default_log_level(log_level);
28
+ }
29
+ Settings::get().debug_print();
30
+ }
31
+
32
+ ApplicationProperties make_application_properties()
33
+ {
34
+ auto& settings = Settings::get();
35
+
36
+ const auto name = settings.get_setting<std::string>("name");
37
+ const auto width = settings.get_setting<size_t>("application.window.width");
38
+ const auto height = settings.get_setting<size_t>("application.window.height");
39
+
40
+ return ApplicationProperties{
41
+ .name = STRING_ID(name.value()),
42
+ .width = width.value(),
43
+ .height = height.value()
44
+ };
45
+ }
46
+
47
+ std::unique_ptr<Application> portal::create_application(int, char**)
48
+ {
49
+ initialize_settings();
50
+ initialize_logger();
51
+
52
+ const auto prop = make_application_properties();
53
+ auto engine = std::make_unique<Engine>(prop);
54
+
55
+ return engine;
56
+ }
@@ -1,11 +1,13 @@
1
1
  import logging
2
2
  import pathlib
3
3
  from typing import Annotated
4
+ from importlib.metadata import version as meta_version
4
5
 
5
6
  import appdirs
6
7
  import typer
7
8
  from pydantic_settings import BaseSettings
8
9
 
10
+ from installer.configurator_factory import ConfiguratorFactory
9
11
  from portal_tool.installer.repo.repo_maker import RepoMaker
10
12
  from portal_tool.installer.installer import Installer
11
13
  from portal_tool.git_manager import GitManager
@@ -20,6 +22,12 @@ global_working_directory = pathlib.Path.cwd()
20
22
  framework: PortalFramework
21
23
 
22
24
 
25
+ def version_callback(value: bool):
26
+ if value:
27
+ typer.echo(f"Portal Tool version: {meta_version('portal_tool')}")
28
+ raise typer.Exit()
29
+
30
+
23
31
  class Settings(BaseSettings):
24
32
  registry_url: str = "github.com:JonatanNevo/portal-vcpkg-registry"
25
33
  examples_url: str = "github.com:JonatanNevo/portal-examples"
@@ -134,7 +142,18 @@ def init(
134
142
 
135
143
 
136
144
  @app.callback()
137
- def main():
145
+ def main(
146
+ version: Annotated[
147
+ bool | None,
148
+ typer.Option(
149
+ "--version",
150
+ "-v",
151
+ callback=version_callback,
152
+ is_eager=True,
153
+ help="Show the version and exit.",
154
+ ),
155
+ ] = None,
156
+ ):
138
157
  logging.basicConfig(level=logging.INFO)
139
158
 
140
159
 
@@ -1,130 +0,0 @@
1
- import os
2
- import pathlib
3
- import platform
4
- import shlex
5
- import subprocess
6
-
7
- import typer
8
-
9
- from portal_tool.installer.configurator_factory import ConfiguratorFactory
10
- from portal_tool.installer.repo.build_models import (
11
- CMakePresets,
12
- ConfigurePreset,
13
- BuildPreset,
14
- )
15
-
16
-
17
- class RepoMaker:
18
- """
19
- A repo consists of:
20
- - vcpkg (either submodule or globally installed)
21
- - src
22
- - resources
23
- - vcpkg configuration
24
- - cmake configuration
25
- """
26
-
27
- def __init__(self, path: pathlib.Path):
28
- self.configurator = ConfiguratorFactory().create(False)
29
- self.presets = CMakePresets()
30
-
31
- self.name = typer.prompt("Project Name")
32
- self.base_path = pathlib.Path(typer.prompt("Project Location", default=path))
33
-
34
- self.vcpkg_toolchain_location = "{}/scripts/buildsystems/vcpkg.cmake"
35
-
36
- self._configure_git()
37
- self._setup_vcpkg()
38
- self._create_repo_from_template()
39
- self._configure_build_system()
40
-
41
- def _configure_git(self) -> None:
42
- if (self.base_path / ".git").exists():
43
- proceed = typer.confirm(
44
- "Found existing git repo, would you like to continue?"
45
- )
46
- if not proceed:
47
- raise typer.Abort("Aborting repo creation.")
48
- return
49
-
50
- typer.echo(f"Initializing git repo in: {self.base_path}")
51
- subprocess.check_output(shlex.split(f'git -C "{self.base_path}" init'))
52
-
53
- def _setup_vcpkg(self) -> None:
54
- vcpkg_root, found_using_env = self.configurator.find_vcpkg_root()
55
-
56
- use_global = False
57
- if vcpkg_root:
58
- use_global = typer.confirm(
59
- f"Found global vcpkg, [{vcpkg_root}] would you like to use? (if not, a local submodule will be created)"
60
- )
61
-
62
- if not use_global:
63
- typer.echo(f"Creating vcpkg submodule in: {self.base_path / 'vcpkg'}")
64
- try:
65
- subprocess.check_output(
66
- shlex.split(
67
- f'git -C "{self.base_path}" submodule add https://github.com/microsoft/vcpkg "{"vcpkg"}"'
68
- )
69
- )
70
- except subprocess.CalledProcessError:
71
- typer.echo("Failed to add submodule, please add it manually.")
72
-
73
- self.vcpkg_toolchain_location = self.vcpkg_toolchain_location.format(
74
- (
75
- "$env{VCPKG_ROOT}"
76
- if found_using_env
77
- else (pathlib.Path(os.path.expanduser("~")) / ".vcpkg").as_posix()
78
- )
79
- if use_global
80
- else "${sourceDir}/vcpkg"
81
- )
82
-
83
- def _create_repo_from_template(self) -> None:
84
- base = ConfigurePreset(
85
- name="base",
86
- hidden=True,
87
- binary_dir="${sourceDir}/build/${presetName}",
88
- cache_variables={
89
- "CMAKE_TOOLCHAIN_FILE": self.vcpkg_toolchain_location,
90
- "CMAKE_CONFIGURATION_TYPES": "Debug;RelWithDebInfo;Release",
91
- },
92
- )
93
-
94
- # TODO: determine generator (ninja-multi, xcode, vs)
95
- ninja_multi = ConfigurePreset(
96
- name="ninja-multi", inherits=[base.name], generator="Ninja Multi-Config"
97
- )
98
-
99
- if platform.system() == "Linux":
100
- ninja_multi.environment = {
101
- "CC": "clang",
102
- "CXX": "clang++",
103
- "VCPKG_KEEP_ENV_VARS": "CC;CXX",
104
- **(ninja_multi.environment if ninja_multi.environment else {}),
105
- }
106
-
107
- self.presets.configure_presets = [base, ninja_multi]
108
-
109
- self.presets.build_presets = [
110
- BuildPreset(
111
- name="debug", configurePreset=ninja_multi.name, configuration="Debug"
112
- ),
113
- BuildPreset(
114
- name="development",
115
- configurePreset=ninja_multi.name,
116
- configuration="RelWithDebInfo",
117
- ),
118
- BuildPreset(
119
- name="release",
120
- configurePreset=ninja_multi.name,
121
- configuration="Release",
122
- ),
123
- ]
124
-
125
- (self.base_path / "CMakePresets.json").write_text(
126
- self.presets.model_dump_json(indent=4, exclude_none=True)
127
- )
128
-
129
- def _configure_build_system(self) -> None:
130
- pass