Skip to content

Commit

Permalink
[builds_scripts] fixed all cases where a whitespace in the path would…
Browse files Browse the repository at this point in the history
… prevent project generation
  • Loading branch information
PanosK92 committed Jan 20, 2025
1 parent e5f09dc commit 8046e53
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 113 deletions.
98 changes: 63 additions & 35 deletions build_scripts/file_utilities.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
#Copyright(c) 2016-2024 Panos Karabelas
# Copyright(c) 2016-2025 Panos Karabelas
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
#copies of the Software, and to permit persons to whom the Software is furnished
#to do so, subject to the following conditions :
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import sys
import subprocess
import hashlib
import importlib
import os
import requests
import shutil
import stat
import subprocess
import sys
from pathlib import Path

def install_and_import(package):
try:
Expand All @@ -33,11 +39,6 @@ def install_and_import(package):
install_and_import('tqdm')
install_and_import('requests')

import os
import hashlib
from tqdm import tqdm
import requests

def calculate_file_hash(file_path):
hash_func = hashlib.new("sha256")
with open(file_path, "rb") as f:
Expand All @@ -56,6 +57,7 @@ def download_file(url, destination, expected_hash):
response = requests.get(url, stream=True)
total_size = int(response.headers.get('content-length', 0))
block_size = 1024
from tqdm import tqdm
t = tqdm(total=total_size, unit='iB', unit_scale=True)

with open(destination, 'wb') as f:
Expand All @@ -73,27 +75,53 @@ def download_file(url, destination, expected_hash):
return

def extract_archive(archive_path, destination_path, is_windows, use_working_dir=False):
# Determine the path to 7z based on the use_working_dir flag
if use_working_dir:
seven_zip_exe = '7z.exe' if is_windows else '7za'
else:
exe_dir = os.path.join(os.getcwd(), 'build_scripts')
seven_zip_exe = os.path.join(exe_dir, '7z.exe' if is_windows else '7za')
# determine the path to 7z based on the use_working_dir flag
seven_zip_exe = Path("build_scripts") / ("7z.exe" if is_windows else "7za")
seven_zip_exe = seven_zip_exe.resolve() if use_working_dir else seven_zip_exe

# convert paths to string for subprocess, ensuring they are quoted if they contain spaces
archive_path_str = f'"{Path(archive_path).resolve()}"'
destination_path_str = f'"{Path(destination_path).resolve()}"'

# Construct the command
cmd = f"{seven_zip_exe} x {archive_path} -o{destination_path} -aoa"
# construct the command as a string with quoted paths
cmd = f'{str(seven_zip_exe)} x {archive_path_str} -o{destination_path_str} -aoa'

print(f"Extracting {archive_path} to {destination_path} using: {seven_zip_exe}")

try:
# Execute the command
result = subprocess.run(cmd, check=True, shell=True, text=True, capture_output=True)
# execute the command with shell=True to handle paths with spaces
result = subprocess.run(cmd, check=True, shell=True, capture_output=True, text=True)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"An error occurred while extracting: {e}")
print(f"Error output: {e.stderr}")
raise # Re-raise the exception for higher-level error handling if needed

raise # re-raise the exception for higher-level error handling if needed
except FileNotFoundError:
print(f"The 7z executable was not found at {seven_zip_exe}. Please check the path or installation.")
raise
raise

def copy(source, destination):
def on_rm_error(func, path, exc_info):
os.chmod(path, stat.S_IWRITE)
func(path)

source_path = Path(source).resolve()
dest_path = Path(destination).resolve()

# check if source is a directory or file
if source_path.is_dir():
# if source is a directory, ensure destination is a directory too
dest_path.mkdir(parents=True, exist_ok=True) # Create the destination directory if it doesn't exist
print(f"Copying directory \"{source_path}\" to directory \"{dest_path}\"...")
shutil.rmtree(str(dest_path), onerror=on_rm_error)
shutil.copytree(str(source_path), str(dest_path), dirs_exist_ok=True)
elif source_path.is_file():
# if source is a file, ensure the parent directory of the destination exists
dest_path.parent.mkdir(parents=True, exist_ok=True) # Create parent directory if it doesn't exist
target = dest_path if dest_path.is_file() else dest_path / source_path.name
print(f"Copying file \"{source_path}\" to \"{target}\"...")
shutil.copy2(str(source_path), str(target))
else:
print(f"Error: Source '{source_path}' is neither a file nor a directory.")
return False
return True
149 changes: 72 additions & 77 deletions build_scripts/generate_project_files.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,111 @@
#Copyright(c) 2016-2025 Panos Karabelas
# Copyright(c) 2016-2025 Panos Karabelas
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
#copies of the Software, and to permit persons to whom the Software is furnished
#to do so, subject to the following conditions :
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import shutil
import sys
import stat
import subprocess
import sys
from pathlib import Path

import file_utilities

paths = {
"binaries": {
"data": Path("binaries/data"),
"models": Path("binaries/project/models"),
"music": Path("binaries/project/music"),
"terrain": Path("binaries/project/terrain"),
"materials": Path("binaries/project/materials"),
"data": Path("binaries") / "data",
"models": Path("binaries") / "project" / "models",
"music": Path("binaries") / "project" / "music",
"terrain": Path("binaries") / "project" / "terrain",
"materials": Path("binaries") / "project" / "materials",
},
"third_party_libs": {
"dx": Path("third_party/libraries/dxcompiler.dll"),
"fmod": Path("third_party/libraries/fmod.dll"),
"fmod_debug": Path("third_party/libraries/fmodL.dll"),
"dx": Path("third_party") / "libraries" / "dxcompiler.dll",
"fmod": Path("third_party") / "libraries" / "fmod.dll",
"fmod_debug": Path("third_party") / "libraries" / "fmodL.dll",
},
"assets": {
"models": Path("assets/models"),
"music": Path("assets/music"),
"terrain": Path("assets/terrain"),
"materials": Path("assets/materials"),
"models": Path("assets") / "models",
"music": Path("assets") / "music",
"terrain": Path("assets") / "terrain",
"materials": Path("assets") / "materials",
},
}

def is_directory(path):
if not os.path.exists(path):
return os.path.splitext(path)[1] == ""
return os.path.isdir(path)
def generate_project_files():
# determine if we're using Windows or another platform
is_windows = sys.argv[1].startswith("vs") # Assuming 'vs' prefix for Visual Studio

def copy(source, destination):
def on_rm_error(func, path, exc_info):
# make the file writable if it's read-only
os.chmod(path, stat.S_IWRITE)
func(path)
# construct the command, stripping any surrounding quotes from arguments
premake_exe = Path.cwd() / "build_scripts" / ("premake5.exe" if is_windows else "premake5")
premake_lua = Path("build_scripts") / "premake.lua"

if os.path.isfile(source):
if is_directory(destination):
dest_file = os.path.join(destination, os.path.basename(source))
else:
dest_file = destination
print(f"Copying file \"{source}\" to \"{dest_file}\"...")
if os.path.exists(dest_file):
os.chmod(dest_file, stat.S_IWRITE) # make the file writable if it exists
shutil.copy2(source, dest_file)
elif is_directory(source) and is_directory(destination):
print(f"Copying directory \"{source}\" to directory \"{destination}\"...")
if os.path.exists(destination):
shutil.rmtree(destination, onerror=on_rm_error)
shutil.copytree(source, destination, dirs_exist_ok=True)
else:
print(f"Error: {source} and {destination} are not compatible.")
return False
return True

def generate_project_files():
cmd = (
f'build_scripts\\premake5.exe --file="build_scripts\\premake.lua" "{sys.argv[1]}" "{sys.argv[2]}"'
if sys.argv[1] == "vs2022"
else f'premake5 --file="build_scripts/premake.lua" "{sys.argv[1]}" "{sys.argv[2]}"'
)
subprocess.Popen(cmd, shell=True).communicate()
# remove quotes if they exist around sys.argv[1] and sys.argv[2]
action = sys.argv[1].strip('"')
platform = sys.argv[2].strip('"')

# construct the command as a string with quoted paths
cmd = f'"{str(premake_exe)}" --file="{str(premake_lua)}" "{action}" "{platform}"'

print("Running command:", cmd)

if sys.argv[1] == "vs2022" and not os.path.exists("spartan.sln"):
print("Error: spartan.sln not generated.")
try:
result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error occurred while generating project files: {e}")
print(f"Error output: {e.stderr}")
sys.exit(1)
elif sys.argv[1] != "vs2022" and not os.path.exists("Makefile") and not os.path.exists("editor/Makefile") and not os.path.exists("runtime/Makefile"):
print("Error: makefiles not generated")
except Exception as e:
print(f"An unexpected error occurred: {e}")
sys.exit(1)

# Check for generated files based on the action
if action == "vs2022":
if not Path("spartan.sln").exists():
print("Error: spartan.sln not generated.")
sys.exit(1)
else:
makefiles = [Path("Makefile"), Path("editor") / "Makefile", Path("runtime") / "Makefile"]
if not any(m.exists() for m in makefiles):
print("Error: makefiles not generated")
sys.exit(1)

def main():
is_ci = "ci" in sys.argv

print("\n1. Create binaries folder with the required data files...\n")
copy("data", paths["binaries"]["data"])
copy("build_scripts/download_assets.py", "binaries/")
copy("build_scripts/file_utilities.py", "binaries/")
copy("build_scripts/7z.exe", "binaries/")
copy("build_scripts/7z.dll", "binaries/")
file_utilities.copy("data", paths["binaries"]["data"])
file_utilities.copy(Path("build_scripts") / "download_assets.py", "binaries")
file_utilities.copy(Path("build_scripts") / "file_utilities.py", "binaries")
file_utilities.copy(Path("build_scripts") / "7z.exe", "binaries")
file_utilities.copy(Path("build_scripts") / "7z.dll", "binaries")

print("\n2. Download and extract libraries...")
library_url = 'https://www.dropbox.com/scl/fi/zq64yfpbly1goahmanm4r/libraries.7z?rlkey=m90lngvaosc9i3w8k16f1e1r6&st=5jm4fmqv&dl=1'
library_destination = 'third_party/libraries/libraries.7z'
library_destination = Path("third_party") / "libraries" / "libraries.7z"
library_expected_hash = '8a20305ee9658dfdfba2aea88f26e6ee3d1330d7e6d26f42bc07bb76150ff1c5'
file_utilities.download_file(library_url, library_destination, library_expected_hash)
file_utilities.extract_archive("third_party/libraries/libraries.7z", "third_party/libraries/", sys.argv[1] == "vs2022", False)
file_utilities.download_file(library_url, str(library_destination), library_expected_hash)
file_utilities.extract_archive(str(library_destination), str(Path("third_party") / "libraries"), sys.argv[1] == "vs2022", False)

print("3. Copying required DLLs to the binary directory...")
for lib in paths["third_party_libs"].values():
copy(lib, Path("binaries"))
file_utilities.copy(lib, Path("binaries"))

print("\n4. Generate project files...\n")
generate_project_files()
Expand Down
27 changes: 26 additions & 1 deletion generate_vs2022_vulkan.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# Copyright(c) 2016-2025 Panos Karabelas
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import subprocess
import sys
from pathlib import Path

def main():
# get the directory of the current script
script_dir = Path(__file__).parent
os.chdir(script_dir)

# define the path to the script to run
script = script_dir / "build_scripts" / "generate_project_files.py"

# run the script with specific arguments
subprocess.Popen([sys.executable, str(script), "vs2022", "vulkan_windows"]).communicate()

# exit the script
sys.exit(0)

if __name__ == "__main__":
main()
main()

0 comments on commit 8046e53

Please sign in to comment.