Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,118 @@
#include "build_common.h"
#include "platform/platform.hpp"
#include "base/file_name_utils.hpp"
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QProcess>
#include <QtCore/QProcessEnvironment>
#include <exception>
#include <iomanip> // std::quoted
#include <regex>
#include <string>
QString ExecProcess(QString const & program, std::initializer_list<QString> args, QProcessEnvironment const * env)
{
// Quote all arguments.
QStringList qargs(args);
for (auto i = qargs.begin(); i != qargs.end(); ++i)
*i = "\"" + *i + "\"";
QProcess p;
if (nullptr != env)
p.setProcessEnvironment(*env);
p.start(program, qargs, QIODevice::ReadOnly);
p.waitForFinished(-1);
int const exitCode = p.exitCode();
QString output = p.readAllStandardOutput();
QString const error = p.readAllStandardError();
if (exitCode != 0)
{
QString msg = "Error: " + program + " " + qargs.join(" ") + "\nReturned " + QString::number(exitCode);
if (!output.isEmpty())
msg += "\n" + output;
if (!error.isEmpty())
msg += "\nSTDERR:\n" + error;
throw std::runtime_error(msg.toStdString());
}
if (!error.isEmpty())
{
QString const msg = "STDERR with a zero exit code:\n" + program + " " + qargs.join(" ");
throw std::runtime_error(msg.toStdString());
}
return output;
}
bool CopyFile(QString const & oldFile, QString const & newFile)
{
if (oldFile == newFile)
return true;
if (!QFile::exists(oldFile))
return false;
if (QFile::exists(newFile) && !QFile::remove(newFile))
return false;
return QFile::copy(oldFile, newFile);
}
void CopyFromResources(QString const & name, QString const & output)
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
if (!CopyFile(JoinPathQt({resourceDir, name}), JoinPathQt({output, name})))
throw std::runtime_error(std::string("Cannot copy file ") + name.toStdString() + " to " + output.toStdString());
}
void CopyToResources(QString const & name, QString const & input, QString const & newName)
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
if (!CopyFile(JoinPathQt({input, name}), JoinPathQt({resourceDir, newName.isEmpty() ? name : newName})))
throw std::runtime_error(std::string("Cannot copy file ") + name.toStdString() + " from " + input.toStdString());
}
QString JoinPathQt(std::initializer_list<QString> folders)
{
QString result;
bool firstInserted = false;
for (auto it = folders.begin(); it != folders.end(); ++it)
{
if (it->isEmpty() || *it == QDir::separator())
continue;
if (firstInserted)
result.append(QDir::separator());
result.append(*it);
firstInserted = true;
}
return QDir::cleanPath(result);
}
QString GetExternalPath(QString const & name, QString const & primaryPath, QString const & secondaryPath)
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
QString path = JoinPathQt({resourceDir, primaryPath, name});
if (!QFileInfo::exists(path))
path = JoinPathQt({resourceDir, secondaryPath, name});
// Special case for looking for in application folder.
if (!QFileInfo::exists(path) && secondaryPath.isEmpty())
{
std::string const appPath = QCoreApplication::applicationDirPath().toStdString();
std::regex re("(/[^/]*\\.app)");
std::smatch m;
if (std::regex_search(appPath, m, re) && m.size() > 0)
path.fromStdString(base::JoinPath(m[0], name.toStdString()));
}
return path;
}
QString GetProtobufEggPath()
{
return GetExternalPath("protobuf-3.3.0-py2.7.egg", "kothic", "../3party/protobuf");
}

View file

@ -0,0 +1,24 @@
#pragma once
#include <QtCore/QString>
#include <initializer_list>
#include <string>
#include <utility>
class QProcessEnvironment;
// Returns stdout output of the program, throws std::runtime_error in case of non-zero exit code.
// Quotes all arguments to avoid issues with space-containing paths.
QString ExecProcess(QString const & program, std::initializer_list<QString> args, QProcessEnvironment const * env = nullptr);
bool CopyFile(QString const & oldFile, QString const & newFile);
void CopyFromResources(QString const & name, QString const & output);
void CopyToResources(QString const & name, QString const & input, QString const & newName = "");
QString JoinPathQt(std::initializer_list<QString> folders);
QString GetExternalPath(QString const & name, QString const & primaryPath,
QString const & secondaryPath);
QString GetProtobufEggPath();

View file

@ -0,0 +1,64 @@
#include "build_drules.h"
#include "build_common.h"
#include "platform/platform.hpp"
#include <exception>
#include <fstream>
#include <streambuf>
#include <string>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QProcessEnvironment>
namespace build_style
{
void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir)
{
QString const outputTemplate = JoinPathQt({outputDir, "drules_proto_design"});
QString const outputFile = outputTemplate + ".bin";
// Caller ensures that output directory is clear
if (QFile(outputFile).exists())
throw std::runtime_error("Output directory is not clear");
// Add path to the protobuf EGG in the PROTOBUF_EGG_PATH environment variable
QProcessEnvironment env{QProcessEnvironment::systemEnvironment()};
env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath());
// Run the script
(void)ExecProcess("python",
{
GetExternalPath("libkomwm.py", "kothic/src", "../tools/kothic/src"),
"-s",
mapcssFile,
"-o",
outputTemplate,
"-x",
"True",
},
&env);
// Ensure that generated file is not empty.
if (QFile(outputFile).size() == 0)
throw std::runtime_error("Drawing rules file has zero size");
}
void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir)
{
CopyFromResources("mapcss-mapping.csv", outputDir);
CopyFromResources("mapcss-dynamic.txt", outputDir);
BuildDrawingRulesImpl(mapcssFile, outputDir);
}
void ApplyDrawingRules(QString const & outputDir)
{
CopyToResources("drules_proto_design.bin", outputDir);
CopyToResources("classificator.txt", outputDir);
CopyToResources("types.txt", outputDir);
CopyToResources("patterns.txt", outputDir, "patterns_design.txt");
CopyToResources("colors.txt", outputDir, "colors_design.txt");
}
} // namespace build_style

View file

@ -0,0 +1,9 @@
#pragma once
#include <QtCore/QString>
namespace build_style
{
void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir);
void ApplyDrawingRules(QString const & outputDir);
} // build_style

View file

@ -0,0 +1,27 @@
#include "build_statistics.h"
#include "build_common.h"
#include "platform/platform.hpp"
#include <QtCore/QDir>
#include <exception>
#include <string>
namespace build_style
{
QString RunBuildingPhonePack(QString const & stylesDir, QString const & targetDir)
{
using std::to_string, std::runtime_error;
if (!QDir(stylesDir).exists())
throw runtime_error("Styles directory does not exist " + stylesDir.toStdString());
if (!QDir(targetDir).exists())
throw runtime_error("target directory does not exist" + targetDir.toStdString());
return ExecProcess("python",
{GetExternalPath("generate_styles_override.py", "", "../tools/python"), stylesDir, targetDir});
}
} // namespace build_style

View file

@ -0,0 +1,8 @@
#pragma once
#include <QString>
namespace build_style
{
QString RunBuildingPhonePack(QString const & stylesFolder, QString const & targetFolder);
} // namespace build_style

View file

@ -0,0 +1,216 @@
#include "qt/build_style/build_skins.h"
#include "qt/build_style/build_common.h"
#include "platform/platform.hpp"
#include <algorithm>
#include <array>
#include <exception>
#include <fstream>
#include <functional>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <QtCore/QDir>
namespace
{
enum SkinType
{
SkinMDPI,
SkinHDPI,
SkinXHDPI,
Skin6Plus,
SkinXXHDPI,
SkinXXXHDPI,
// SkinCount MUST BE last
SkinCount
};
using SkinInfo = std::tuple<char const *, int, bool>;
SkinInfo const g_skinInfo[SkinCount] = {
std::make_tuple("mdpi", 18, false), std::make_tuple("hdpi", 27, false), std::make_tuple("xhdpi", 36, false),
std::make_tuple("6plus", 43, false), std::make_tuple("xxhdpi", 54, false), std::make_tuple("xxxhdpi", 64, false),
};
std::array<SkinType, SkinCount> const g_skinTypes = {{
SkinMDPI,
SkinHDPI,
SkinXHDPI,
Skin6Plus,
SkinXXHDPI,
SkinXXXHDPI,
}};
inline char const * SkinSuffix(SkinType s)
{
return std::get<0>(g_skinInfo[s]);
}
inline int SkinSize(SkinType s)
{
return std::get<1>(g_skinInfo[s]);
}
inline bool SkinCoorrectColor(SkinType s)
{
return std::get<2>(g_skinInfo[s]);
}
QString GetSkinGeneratorPath()
{
QString const path = GetExternalPath("skin_generator_tool", "skin_generator_tool.app/Contents/MacOS", "");
if (path.isEmpty())
throw std::runtime_error("Can't find skin_generator_tool");
ASSERT(QFileInfo::exists(path), (path.toStdString()));
return path;
}
class RAII
{
public:
RAII(std::function<void()> && f) : m_f(std::move(f)) {}
~RAII() { m_f(); }
private:
std::function<void()> const m_f;
};
std::string trim(std::string && s)
{
s.erase(std::remove_if(s.begin(), s.end(), &isspace), s.end());
return std::move(s);
}
} // namespace
namespace build_style
{
std::unordered_map<std::string, int> GetSkinSizes(QString const & file)
{
std::unordered_map<std::string, int> skinSizes;
for (SkinType s : g_skinTypes)
skinSizes.insert(std::make_pair(SkinSuffix(s), SkinSize(s)));
try
{
std::ifstream ifs(file.toStdString());
std::string line;
while (std::getline(ifs, line))
{
size_t const pos = line.find('=');
if (pos == std::string::npos)
continue;
std::string name(line.begin(), line.begin() + pos);
std::string valueTxt(line.begin() + pos + 1, line.end());
name = trim(std::move(name));
int value = std::stoi(trim(std::move(valueTxt)));
if (value <= 0)
continue;
skinSizes[name] = value;
}
}
catch (std::exception const & e)
{
// reset
for (SkinType s : g_skinTypes)
skinSizes[SkinSuffix(s)] = SkinSize(s);
}
return skinSizes;
}
void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, bool colorCorrection,
QString const & outputDir)
{
QString const symbolsDir = JoinPathQt({styleDir, "symbols"});
// Check symbols directory exists
if (!QDir(symbolsDir).exists())
throw std::runtime_error("Symbols directory does not exist");
// Caller ensures that output directory is clear
if (QDir(outputDir).exists())
throw std::runtime_error("Output directory is not clear");
// Create output skin directory
if (!QDir().mkdir(outputDir))
throw std::runtime_error("Cannot create output skin directory");
// Create symbolic link for symbols/png
QString const pngOriginDir = styleDir + suffix;
QString const pngDir = JoinPathQt({styleDir, "symbols", "png"});
QFile::remove(pngDir);
if (!QFile::link(pngOriginDir, pngDir))
throw std::runtime_error("Unable to create symbols/png link");
RAII const cleaner([=]() { QFile::remove(pngDir); });
QString const strSize = QString::number(size);
// Run the script.
(void)ExecProcess(GetSkinGeneratorPath(), {
"--symbolWidth",
strSize,
"--symbolHeight",
strSize,
"--symbolsDir",
symbolsDir,
"--skinName",
JoinPathQt({outputDir, "basic"}),
"--skinSuffix=",
"--colorCorrection",
(colorCorrection ? "true" : "false"),
});
// Check if files were created.
if (QFile(JoinPathQt({outputDir, "symbols.png"})).size() == 0 ||
QFile(JoinPathQt({outputDir, "symbols.sdf"})).size() == 0)
{
throw std::runtime_error("Skin files have not been created");
}
}
void BuildSkins(QString const & styleDir, QString const & outputDir)
{
QString const resolutionFilePath = JoinPathQt({styleDir, "resolutions.txt"});
auto const resolution2size = GetSkinSizes(resolutionFilePath);
for (SkinType s : g_skinTypes)
{
QString const suffix = SkinSuffix(s);
QString const outputSkinDir = JoinPathQt({outputDir, "symbols", suffix, "design"});
int const size = resolution2size.at(suffix.toStdString()); // SkinSize(s);
bool const colorCorrection = SkinCoorrectColor(s);
BuildSkinImpl(styleDir, suffix, size, colorCorrection, outputSkinDir);
}
}
void ApplySkins(QString const & outputDir)
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
for (SkinType s : g_skinTypes)
{
QString const suffix = SkinSuffix(s);
QString const outputSkinDir = JoinPathQt({outputDir, "symbols", suffix, "design"});
QString const resourceSkinDir = JoinPathQt({resourceDir, "symbols", suffix, "design"});
if (!QFileInfo::exists(resourceSkinDir) && !QDir().mkdir(resourceSkinDir))
throw std::runtime_error("Cannot create resource skin directory: " + resourceSkinDir.toStdString());
if (!CopyFile(JoinPathQt({outputSkinDir, "symbols.png"}), JoinPathQt({resourceSkinDir, "symbols.png"})) ||
!CopyFile(JoinPathQt({outputSkinDir, "symbols.sdf"}), JoinPathQt({resourceSkinDir, "symbols.sdf"})))
{
throw std::runtime_error("Cannot copy skins files");
}
}
}
} // namespace build_style

View file

@ -0,0 +1,9 @@
#pragma once
#include <QtCore/QString>
namespace build_style
{
void BuildSkins(QString const & styleDir, QString const & outputDir);
void ApplySkins(QString const & outputDir);
} // namespace build_style

View file

@ -0,0 +1,46 @@
#include "build_statistics.h"
#include "build_common.h"
#include "platform/platform.hpp"
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QProcessEnvironment>
#include <exception>
#include <string>
namespace build_style
{
QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & drulesFile)
{
if (!QFile(mapcssMappingFile).exists())
throw std::runtime_error("mapcss-mapping file does not exist at " + mapcssMappingFile.toStdString());
if (!QFile(drulesFile).exists())
throw std::runtime_error("drawing-rules file does not exist at " + drulesFile.toStdString());
// Add path to the protobuf EGG in the PROTOBUF_EGG_PATH environment variable.
QProcessEnvironment env{QProcessEnvironment::systemEnvironment()};
env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath());
// Run the script.
return ExecProcess("python",
{
GetExternalPath("drules_info.py", "kothic/src", "../tools/python/stylesheet"),
mapcssMappingFile,
drulesFile,
},
&env);
}
QString GetCurrentStyleStatistics()
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
QString const mappingPath = JoinPathQt({resourceDir, "mapcss-mapping.csv"});
QString const drulesPath = JoinPathQt({resourceDir, "drules_proto_design.bin"});
return GetStyleStatistics(mappingPath, drulesPath);
}
} // namespace build_style

View file

@ -0,0 +1,9 @@
#pragma once
#include <QtCore/QString>
namespace build_style
{
QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & drulesFile);
QString GetCurrentStyleStatistics();
} // namespace build_style

View file

@ -0,0 +1,121 @@
#include "build_style.h"
#include "build_common.h"
#include "build_drules.h"
#include "build_skins.h"
#include "platform/platform.hpp"
#include <exception>
#include <future>
#include <string>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFile>
namespace
{
QString GetRecalculateGeometryScriptPath()
{
return GetExternalPath("recalculate_geom_index.py", "", "../tools/python");
}
QString GetGeometryToolPath()
{
return GetExternalPath("generator_tool", "generator_tool.app/Contents/MacOS", "");
}
QString GetGeometryToolResourceDir()
{
return GetExternalPath("", "generator_tool.app/Contents/Resources", "");
}
} // namespace
namespace build_style
{
void BuildAndApply(QString const & mapcssFile)
{
// Ensure mapcss exists
if (!QFile(mapcssFile).exists())
throw std::runtime_error("mapcss files does not exist");
QDir const projectDir = QFileInfo(mapcssFile).absoluteDir();
QString const styleDir = projectDir.absolutePath() + QDir::separator();
QString const outputDir = styleDir + "out" + QDir::separator();
// Ensure output directory is clear
if (QDir(outputDir).exists() && !QDir(outputDir).removeRecursively())
throw std::runtime_error("Unable to remove the output directory");
if (!QDir().mkdir(outputDir))
throw std::runtime_error("Unable to make the output directory");
bool const hasSymbols = QDir(styleDir + "symbols/").exists();
if (hasSymbols)
{
auto future = std::async(std::launch::async, BuildSkins, styleDir, outputDir);
BuildDrawingRules(mapcssFile, outputDir);
future.get(); // may rethrow exception from the BuildSkin
ApplyDrawingRules(outputDir);
ApplySkins(outputDir);
}
else
{
BuildDrawingRules(mapcssFile, outputDir);
ApplyDrawingRules(outputDir);
}
}
void BuildIfNecessaryAndApply(QString const & mapcssFile)
{
if (!QFile(mapcssFile).exists())
throw std::runtime_error("mapcss files does not exist");
QDir const projectDir = QFileInfo(mapcssFile).absoluteDir();
QString const styleDir = projectDir.absolutePath() + QDir::separator();
QString const outputDir = styleDir + "out" + QDir::separator();
if (QDir(outputDir).exists())
{
try
{
ApplyDrawingRules(outputDir);
ApplySkins(outputDir);
}
catch (std::exception const & ex)
{
BuildAndApply(mapcssFile);
}
}
else
{
BuildAndApply(mapcssFile);
}
}
void RunRecalculationGeometryScript(QString const & mapcssFile)
{
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
QString const writableDir = GetPlatform().WritableDir().c_str();
QString const generatorToolPath = GetGeometryToolPath();
QString const appPath = QCoreApplication::applicationFilePath();
QString const geometryToolResourceDir = GetGeometryToolResourceDir();
CopyFromResources("drules_proto_design.bin", geometryToolResourceDir);
CopyFromResources("classificator.txt", geometryToolResourceDir);
CopyFromResources("types.txt", geometryToolResourceDir);
(void)ExecProcess("python", {
GetRecalculateGeometryScriptPath(),
resourceDir,
writableDir,
generatorToolPath,
appPath,
mapcssFile,
});
}
bool NeedRecalculate = false;
} // namespace build_style

View file

@ -0,0 +1,12 @@
#pragma once
#include <QtCore/QString>
namespace build_style
{
void BuildAndApply(QString const & mapcssFile);
void BuildIfNecessaryAndApply(QString const & mapcssFile);
void RunRecalculationGeometryScript(QString const & mapcssFile);
extern bool NeedRecalculate;
} // namespace build_style

View file

@ -0,0 +1,22 @@
#include "run_tests.h"
#include "platform/platform.hpp"
#include "build_common.h"
namespace build_style
{
std::pair<bool, QString> RunCurrentStyleTests()
{
QString const program = GetExternalPath("style_tests", "style_tests.app/Contents/MacOS", "");
QString const resourcesDir = QString::fromStdString(GetPlatform().ResourcesDir());
QString const output = ExecProcess(program, {
"--user_resource_path=" + resourcesDir,
"--data_path=" + resourcesDir,
});
// Unfortunately test process returns 0 even if some test failed,
// therefore phrase 'All tests passed.' is looked to be sure that everything is OK.
return std::make_pair(output.contains("All tests passed."), output);
}
} // namespace build_style

View file

@ -0,0 +1,10 @@
#pragma once
#include <QtCore/QString>
#include <utility>
namespace build_style
{
std::pair<bool, QString> RunCurrentStyleTests();
} // namespace build_style