Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PPJ support and pre-scan namespace resolution #27

Merged
merged 22 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ on:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
release:
types: [created]

env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
PROJECT_NAME: Caprica
CMAKE_ARGS: "-DCHAMPOLLION_USE_STATIC_RUNTIME:BOOL=TRUE -DENABLE_STATIC_RUNTIME:BOOL=TRUE -DCMAKE_INSTALL_PREFIX:STRING=build/extern -DVCPKG_TARGET_TRIPLET:STRING=x64-windows-static -DCMAKE_TOOLCHAIN_FILE:STRING=C:/vcpkg/scripts/buildsystems/vcpkg.cmake"

jobs:
Expand Down Expand Up @@ -51,8 +54,32 @@ jobs:
uses: actions/[email protected]
with:
# Artifact name
name: Caprica
name: ${{ env.PROJECT_NAME }}
# A file, directory or wildcard pattern that describes what to upload
path: build/Caprica/Release/Caprica.exe
# The desired behavior if no files are found using the provided path.
retention-days: 90


release:
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
needs: build
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.PROJECT_NAME }}
path: artifacts/${{ env.PROJECT_NAME }}
- name: Zip artifacts
run: |
ls -la artifacts/*
cd artifacts/${{ env.PROJECT_NAME }}
zip -r9 "../${{ env.PROJECT_NAME }}-${{ github.ref_name }}.zip" *
- name: Release
uses: nikitalita/[email protected]
with:
files: |
artifacts/${{ env.PROJECT_NAME }}-${{ github.ref_name }}.zip
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ if(CAPRICA_STATIC_LIBRARY)
add_library("${PROJECT_NAME}" STATIC ${HEADER_FILES} ${SOURCE_FILES})
find_package(Boost COMPONENTS container REQUIRED)
find_package(fmt REQUIRED)
find_package(pugixml CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::container fmt::fmt)
target_link_libraries(${PROJECT_NAME} PRIVATE pugixml pugixml::static pugixml::pugixml)

target_include_directories(
"${PROJECT_NAME}"
Expand Down Expand Up @@ -134,13 +136,15 @@ if(CAPRICA_STATIC_LIBRARY)
else()
find_package(Boost COMPONENTS filesystem program_options container REQUIRED)
find_package(fmt REQUIRED)
find_package(pugixml CONFIG REQUIRED)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/Caprica
${Boost_INCLUDE_DIRS}
)
add_subdirectory(Caprica)
add_dependencies(${PROJECT_NAME} Caprica)
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::filesystem Boost::program_options Boost::container fmt::fmt)
target_link_libraries(${PROJECT_NAME} PRIVATE pugixml pugixml::static pugixml::pugixml)

install(
TARGETS Caprica
Expand Down
24 changes: 15 additions & 9 deletions Caprica/common/CapricaConfig.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "CapricaConfig.h"
#include <common/CapricaConfig.h>

#include <common/FSUtils.h>
#include <filesystem>
namespace caprica { namespace conf {

// These should always be defaulted to false/empty, and their real
Expand All @@ -8,10 +10,14 @@ namespace caprica { namespace conf {
namespace General {
bool compileInParallel{ false };
bool quietCompile{ false };
}
bool recursive { false };
std::filesystem::path outputDirectory;
bool anonymizeOutput;
std::vector<std::shared_ptr<IInputFile>> inputFiles;
}

namespace PCompiler {
// pCompiler compatibility mode.
namespace PCompiler {
// pCompiler compatibility mode.
bool pCompilerCompatibilityMode{false};
bool all{false};
bool norecurse{false};
Expand Down Expand Up @@ -46,15 +52,15 @@ namespace EngineLimits {
}

namespace Papyrus {
GameID game;
bool allowCompilerIdentifiers{ false };
GameID game { GameID::UNKNOWN };
bool allowCompilerIdentifiers { false };
bool allowDecompiledStructNameRefs{ false };
bool allowNegativeLiteralAsBinaryOp{ false };
bool enableLanguageExtensions{ false };
bool ignorePropertyNameLocalConflicts{ false };
bool allowImplicitNoneCastsToAnyType{ false };
std::vector<std::string> importDirectories{ };
CapricaUserFlagsDefinition userFlagsDefinition{ };
std::vector<ImportDir> importDirectories {};
CapricaUserFlagsDefinition userFlagsDefinition{};
}

namespace Skyrim {
Expand All @@ -81,4 +87,4 @@ namespace Warnings {
std::unordered_set<size_t> warningsToEnable{ };
}

}}
}}
17 changes: 14 additions & 3 deletions Caprica/common/CapricaConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
#include <unordered_set>
#include <vector>

#include <common/CapricaUserFlagsDefinition.h>
#include "GameID.h"

#include <common/CapricaUserFlagsDefinition.h>
#include <common/parser/PapyrusProject.h>
#include <common/FSUtils.h>
#include <filesystem>
#include <common/CapricaInputFile.h>
namespace caprica { namespace conf {

// Options that don't fit in any other category.
Expand All @@ -17,6 +20,14 @@ namespace General {
extern bool compileInParallel;
// If true, only report failures, not progress.
extern bool quietCompile;
// If true, recurse into subdirectories when compiling.
extern bool recursive;
// self-explanatory
extern std::filesystem::path outputDirectory;
// If true, remove identifying information from the header.
extern bool anonymizeOutput;
// input files
extern std::vector<std::shared_ptr<IInputFile>> inputFiles;
}

// options related to compatibility with PCompiler's CLI parsing and name resolution
Expand Down Expand Up @@ -102,7 +113,7 @@ namespace Papyrus {
extern bool allowImplicitNoneCastsToAnyType;
// The directories to search in for imported types and
// unknown types.
extern std::vector<std::string> importDirectories;
extern std::vector<ImportDir> importDirectories;
// The user flags definition.
extern CapricaUserFlagsDefinition userFlagsDefinition;
}
Expand Down
186 changes: 186 additions & 0 deletions Caprica/common/CapricaInputFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include "CapricaInputFile.h"
#include <common/CapricaConfig.h>
#include <common/CapricaInputFile.h>
#include <common/FSUtils.h>
#include <common/parser/PapyrusProject.h>
#include <filesystem>

namespace caprica {

std::filesystem::path IInputFile::resolved_relative() const {
if (!resolved)
return {};
auto rel = absPath.lexically_relative(absBaseDir);
if (rel == ".")
return {};
return rel;
}

std::filesystem::path IInputFile::resolved_absolute() const {
if (!resolved)
return {};
return absPath;
}

std::filesystem::path IInputFile::resolved_absolute_basedir() const {
if (!resolved)
return {};
return absBaseDir;
}

std::filesystem::path IInputFile::resolved_relative_parent() const {
if (!resolved)
return {};
auto rel = resolved_relative();
if (rel.empty())
return {};
return rel.parent_path();
}

std::filesystem::path IInputFile::find_import_dir(const std::filesystem::path& path) {
for (auto& dir : conf::Papyrus::importDirectories)
if (dirContains(path, dir.resolved_absolute()))
return dir.resolved_absolute();
return {};
}

bool IInputFile::dirContains(const std::filesystem::path& path, const std::filesystem::path& dir) {
if (path.is_absolute()) {
// check if the path is contained in the import directory
auto rel = path.lexically_relative(dir).string();
if (!rel.empty() && !rel.starts_with(".."))
return true;
} else {
if (std::filesystem::exists(dir / path))
return true;
}
return false;
}

IInputFile::IInputFile(const std::filesystem::path& _path, bool noRecurse, const std::filesystem::path& _cwd)
: noRecurse(noRecurse),
rawPath(std::move(_path)),
cwd(_cwd.empty() ? std::filesystem::current_path() : std::move(FSUtils::canonicalFS(_cwd))) {
}

bool IInputFile::exists() const {
if (!resolved)
return false;
return std::filesystem::exists(resolved_absolute());
}

static constexpr char const curDir[3] = { '.', FSUtils::SEP, 0 };
static constexpr char const parent[4] = { '.', '.', FSUtils::SEP, 0 };
std::filesystem::path getCorrectBaseDir(const std::filesystem::path& normalPath,
const std::filesystem::path& absBaseDir) {
// for every 2 ..s in the path, remove a directory from the base dir
auto str = normalPath.string();
auto ret = absBaseDir;
while (str.starts_with(parent)) {
ret = ret.parent_path();
str = str.substr(3);
}
return ret;
}

InputFile::InputFile(const std::filesystem::path& _path, bool noRecurse, const std::filesystem::path& _cwd)
: IInputFile(_path, noRecurse, _cwd) {
requiresPreParse = true; // we always require pre-parse for non-PCompiler-compatible input files
}

bool InputFile::resolve() {
auto normalPath = FSUtils::normalize(rawPath);
if (!normalPath.is_absolute()) {
absPath = FSUtils::canonicalFS(cwd / normalPath);
absBaseDir = find_import_dir(absPath);
if (absBaseDir.empty()) {
absBaseDir = getCorrectBaseDir(normalPath, cwd);
}
} else {
absPath = FSUtils::canonicalFS(normalPath);
absBaseDir = find_import_dir(absPath);
if (absBaseDir.empty()) {
absBaseDir = absPath.parent_path();
}
}

if (std::filesystem::exists(absPath)) {
if (std::filesystem::is_directory(absPath))
isFolder = true;
resolved = true;
return true;
}

return false;
}

ImportDir::ImportDir(const std::filesystem::path& _path, bool noRecurse, const std::filesystem::path& _cwd)
: IInputFile(_path, noRecurse, _cwd) {
requiresPreParse = true; // we always require pre-parse for import dirs
import = true;
isFolder = true;
resolve(); // we resolve import dirs immediately
}

bool ImportDir::resolve() {
if (!rawPath.is_absolute())
absPath = FSUtils::canonicalFS(cwd / rawPath);
else
absPath = FSUtils::canonicalFS(rawPath);
absBaseDir = absPath;
if (std::filesystem::exists(absPath) && std::filesystem::is_directory(absPath)) {
resolved = true;
return true;
}
return false;
}

PCompInputFile::PCompInputFile(const std::filesystem::path& _path,
bool noRecurse,
bool isFolder,
const std::filesystem::path& _cwd)
: IInputFile(_path, noRecurse, _cwd) {
isFolder = isFolder;
}

bool PCompInputFile::resolve() {
std::filesystem::path normalPath = FSUtils::objectNameToPath(rawPath.string());
if (!isFolder && normalPath.extension().empty())
normalPath.replace_extension(".psc");
normalPath = FSUtils::normalize(normalPath);
std::string str = normalPath.string();
// special case for relative paths that contain parent/cwd refs
if (!normalPath.is_absolute() && (str == "." || str == ".." || str.starts_with(curDir) || str.contains(parent))) {
absPath = FSUtils::canonicalFS(cwd / normalPath);
absBaseDir = getCorrectBaseDir(normalPath, cwd);

if (!std::filesystem::exists(absPath))
return false;
resolved = true;
return true;
}

// if this is a relative folder path, and the folder is in the cwd, use cwd as the base dir
if (isFolder && !normalPath.is_absolute() && dirContains(normalPath, cwd))
absBaseDir = getCorrectBaseDir(normalPath, cwd);
else
absBaseDir = find_import_dir(normalPath);

if (absBaseDir.empty())
return false;

if (!normalPath.is_absolute())
absPath = FSUtils::canonicalFS(absBaseDir / normalPath);
else
absPath = FSUtils::canonicalFS(normalPath);

if (!std::filesystem::exists(absPath))
return false;
if (isFolder && !std::filesystem::is_directory(absPath))
return false;

resolved = true;
return true;
}

} // namespace caprica
Loading