Skip to content

Commit

Permalink
Merge pull request #27 from nikitalita/ppj-parsing
Browse files Browse the repository at this point in the history
PPJ support and pre-scan namespace resolution
  • Loading branch information
nikitalita authored Jun 13, 2024
2 parents 2042c90 + c4950d1 commit 588a6dc
Show file tree
Hide file tree
Showing 18 changed files with 1,405 additions and 352 deletions.
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

0 comments on commit 588a6dc

Please sign in to comment.