diff --git a/.gitignore b/.gitignore index f281046..e8178b8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,12 @@ *.ilk [Bb]in/* !smp_* -*Caches/ \ No newline at end of file +*Caches/ +!bin2c.exe +!source2c.exe +!opencl_source2c.props +!opencl_source2c.targets +!opencl_source2c.xml +!cuda_bin2c.props +!cuda_bin2c.targets +!cuda_bin2c.xml diff --git a/README.md b/README.md index 7243fe1..21c7597 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,31 @@ FFVS-Project-Generator [![GitHub issues](https://img.shields.io/github/issues/ShiftMediaProject/FFVS-Project-Generator.svg)](https://github.com/ShiftMediaProject/FFVS-Project-Generator/issues) [![license](https://img.shields.io/github/license/ShiftMediaProject/FFVS-Project-Generator.svg)](https://github.com/ShiftMediaProject/FFVS-Project-Generator) [![donate](https://img.shields.io/badge/donate-link-brightgreen.svg)](https://shiftmediaproject.github.io/8-donate/) + +## Major Update - Supports FFmpeg 8.x and beyond + +**Changes** + +- **Adds support for compilations of fftools/resources via bin2c.exe** + (supersedes #83) +- **Adds a `smp_common.props` file** + This is imported by all projects and allows to apply changes easily and in a persistent way - i.e. your modifications do not get lost when regenerating the projects +- **Adds support for libvpl (OneVPL)** + This is the new dispatcher for Intel QSV hardware acceleration, which replaces libmfx +- **Adds support for shaderc dependency** +- **Adds support for shader (.comp) file conversion to C strings** +- **Adds support for OpenCL (.cl) file conversion to C strings** +- **Adds support for CUDA PTX compile via NVCC and conversion to C strings** + (supersedes #82) +- **New option to specify a custom tesseract library name** + (tesseract library naming is messy) +- **Detect artifacts from configure in the ffmpeg tree** + The generator doesn't work properly when configure has been run in the FFmpeg source dir. + Added detection for this case and warning will be shown +- **Adds support for Vulkan (filters) and libplacebo** + A repo will be provided with an adapted libplacebo which can be compiled on Windows without MSYS2 + + ## About The FFmpeg VS Project Generator is a standalone program that can be used to create a custom Visual Studio project within a FFmpeg source code distribution. This program allows for the created Visual Studio project to be customised using virtually any of the options supported by FFmpegs default configure script. This allows for selecting which dependency libraries and codec/format support should be built into the created project file. With the output project FFmpeg libraries and programs can be built and debugged directly within Visual Studio. @@ -107,4 +132,4 @@ https://github.com/ShiftMediaProject/VSYASM/releases/latest ## License -FFVS-Project-Generator itself is released under [LGPLv2](https://www.gnu.org/licenses/lgpl-2.0.html). The generated output project(s) and source can be used with existing FFmpeg source code such that any resultant binaries created by the generated projects will still conform to the license of the FFmpeg source code itself. This means the output binaries are licensed based on the command line specified when generating the projects (i.e. --enable-gpl etc.). \ No newline at end of file +FFVS-Project-Generator itself is released under [LGPLv2](https://www.gnu.org/licenses/lgpl-2.0.html). The generated output project(s) and source can be used with existing FFmpeg source code such that any resultant binaries created by the generated projects will still conform to the license of the FFmpeg source code itself. This means the output binaries are licensed based on the command line specified when generating the projects (i.e. --enable-gpl etc.). diff --git a/include/configGenerator.h b/include/configGenerator.h index fc56ddd..72c5759 100644 --- a/include/configGenerator.h +++ b/include/configGenerator.h @@ -72,6 +72,7 @@ class ConfigGenerator DefaultValuesList m_replaceList; DefaultValuesList m_replaceListASM; bool m_useNASM{true}; + string m_tesseractName{"tesseract"}; ConfigList m_cachedConfigLists; public: diff --git a/include/projectGenerator.h b/include/projectGenerator.h index e394163..1eff6c1 100644 --- a/include/projectGenerator.h +++ b/include/projectGenerator.h @@ -71,6 +71,7 @@ class ProjectGenerator StaticList m_subDirs; map m_projectLibs; + bool m_addCustomTesseract{false}; const string m_tempDirectory = "FFVSTemp/"; @@ -432,6 +433,11 @@ class ProjectGenerator void outputDefines(const StaticList& definesShared, const StaticList& definesStatic, string& projectTemplate, bool program = false); + /** + * Comment out preprocessor directives inside macro arguments that MSVC cannot handle. + */ + void sanitizeSourceFiles(); + /** * Output asm tools to project template. * @remark Either yasm or nasm tools will be used based on current configuration. @@ -445,6 +451,38 @@ class ProjectGenerator */ void outputCUDATools(string& projectTemplate) const; + /** + * Output OpenCL source2c build customization to project template. + * @remark Copies source2c.exe and opencl_source2c .props/.targets/.xml to the output directory + * and adds the ExtensionSettings/ExtensionTargets import groups. + * @param [in,out] projectTemplate The project template. + */ + void outputOpenCLTools(string& projectTemplate) const; + + /** + * Output SPIRV source2c build customization to project template. + * @remark Copies source2c.exe and spirv_source2c .props/.targets/.xml to the output directory + * and adds the ExtensionSettings/ExtensionTargets import groups. + * @param [in,out] projectTemplate The project template. + */ + void outputSPIRVTools(string& projectTemplate) const; + + /** + * Output resource source files (HTML/CSS) with custom build steps. + * @param [in,out] fileList The list of resource files to process. + * @param [in,out] projectTemplate The project template. + * @param [in,out] filterTemplate The filter template. + * @param [in,out] foundObjects The list of found object files. + * @param [in,out] foundFilters The set of found filters. + * @param staticOnly True to only include in static builds. + * @param sharedOnly True to only include in shared builds. + * @param bit32Only True to only include in 32-bit builds. + * @param bit64Only True to only include in 64-bit builds. + */ + void outputResourceSourceFiles(StaticList& fileList, string& projectTemplate, string& filterTemplate, + StaticList& foundObjects, set& foundFilters, bool staticOnly = false, bool sharedOnly = false, + bool bit32Only = false, bool bit64Only = false) const; + bool outputDependencyLibs(string& projectTemplate, bool winrt, bool program); /** diff --git a/project_generate.sln b/project_generate.sln index cb8ff86..f6e2426 100644 --- a/project_generate.sln +++ b/project_generate.sln @@ -1,9 +1,16 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -VisualStudioVersion = 12.0.30501.0 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11512.155 d18.3 MinimumVisualStudioVersion = 12.0.30501.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project_generate", "project_generate.vcxproj", "{FA1D2C31-D809-4021-9DE4-7552704175EE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "!Config", "!Config", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + newtask\OpenCL_Build.md = newtask\OpenCL_Build.md + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -24,4 +31,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5B060291-F0AE-456C-85C4-754764957A2A} + EndGlobalSection EndGlobal diff --git a/project_generate.vcxproj b/project_generate.vcxproj index 62fe609..e016f9c 100644 --- a/project_generate.vcxproj +++ b/project_generate.vcxproj @@ -1,4 +1,4 @@ - + @@ -39,15 +39,37 @@ + + + + + + + + + + + + + + + + + + + + + {FA1D2C31-D809-4021-9DE4-7552704175EE} ffmpeg_generate project_generate + 10.0 @@ -58,6 +80,7 @@ v141 v140 v120 + v143 Application @@ -67,6 +90,7 @@ v141 v140 v120 + v143 Application @@ -77,6 +101,7 @@ v140 v120 true + v143 Application @@ -87,6 +112,7 @@ v140 v120 true + v143 diff --git a/project_generate.vcxproj.filters b/project_generate.vcxproj.filters index a494315..4f5371c 100644 --- a/project_generate.vcxproj.filters +++ b/project_generate.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -66,6 +66,15 @@ + + + + + + + + + Templates diff --git a/resources/bin2c.exe b/resources/bin2c.exe new file mode 100644 index 0000000..093eb1d Binary files /dev/null and b/resources/bin2c.exe differ diff --git a/resources/cuda_bin2c.props b/resources/cuda_bin2c.props new file mode 100644 index 0000000..8f58962 --- /dev/null +++ b/resources/cuda_bin2c.props @@ -0,0 +1,19 @@ + + + + + $(IntDir)%(Filename).ptx.c + false + $(TLogLocation) + true + true + + true + ClCompile + true + 0 + 1 + "%CUDA_PATH%\bin\nvcc" -gencode arch=compute_60,code=sm_60 -O2 -m64 -ptx -c -o "$(IntDir)%(Filename).ptx" "%(FullPath)" && "$(ProjectDir)bin2c.exe" "$(IntDir)%(Filename).ptx" "%(Outputs)" %(Filename)_ptx + + + diff --git a/resources/cuda_bin2c.targets b/resources/cuda_bin2c.targets new file mode 100644 index 0000000..372fa75 --- /dev/null +++ b/resources/cuda_bin2c.targets @@ -0,0 +1,27 @@ + + + + + + SelectCudaBin2C + + + + + + + + %(CudaBin2C.FullPath) + %(CudaBin2C.AdditionalInputs);$(MSBuildProjectFile) + + Exec CudaBin2C: %(CudaBin2C.FileName) + + + + + + + + + + diff --git a/resources/cuda_bin2c.xml b/resources/cuda_bin2c.xml new file mode 100644 index 0000000..76603dc --- /dev/null +++ b/resources/cuda_bin2c.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/opencl_source2c.props b/resources/opencl_source2c.props new file mode 100644 index 0000000..9629d07 --- /dev/null +++ b/resources/opencl_source2c.props @@ -0,0 +1,19 @@ + + + + + $(IntDir)%(Filename)_cl.c + false + $(TLogLocation) + true + true + + true + ClCompile + true + 0 + 1 + "$(ProjectDir)source2c.exe" "%(FullPath)" "%(Outputs)" + + + diff --git a/resources/opencl_source2c.targets b/resources/opencl_source2c.targets new file mode 100644 index 0000000..daa9a4b --- /dev/null +++ b/resources/opencl_source2c.targets @@ -0,0 +1,27 @@ + + + + + + SelectOpenCLSource2C + + + + + + + + %(OpenCLSource2C.FullPath) + %(OpenCLSource2C.AdditionalInputs);$(MSBuildProjectFile) + + Exec OpenCLSource2C: %(OpenCLSource2C.FileName) + + + + + + + + + + diff --git a/resources/opencl_source2c.xml b/resources/opencl_source2c.xml new file mode 100644 index 0000000..e786ef2 --- /dev/null +++ b/resources/opencl_source2c.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/source2c.exe b/resources/source2c.exe new file mode 100644 index 0000000..fbab31f Binary files /dev/null and b/resources/source2c.exe differ diff --git a/resources/spirv_source2c.props b/resources/spirv_source2c.props new file mode 100644 index 0000000..9d61c14 --- /dev/null +++ b/resources/spirv_source2c.props @@ -0,0 +1,19 @@ + + + + + $(IntDir)%(Filename)_comp.c + false + $(TLogLocation) + true + true + + true + ClCompile + true + 0 + 1 + "$(ProjectDir)source2c.exe" "%(FullPath)" "%(Outputs)" + + + diff --git a/resources/spirv_source2c.targets b/resources/spirv_source2c.targets new file mode 100644 index 0000000..6fa9b27 --- /dev/null +++ b/resources/spirv_source2c.targets @@ -0,0 +1,27 @@ + + + + + + SelectSPIRVSource2C + + + + + + + + %(SPIRVSource2C.FullPath) + %(SPIRVSource2C.AdditionalInputs);$(MSBuildProjectFile) + + Exec SPIRVSource2C: %(SPIRVSource2C.FileName) + + + + + + + + + + diff --git a/resources/spirv_source2c.xml b/resources/spirv_source2c.xml new file mode 100644 index 0000000..9f75dbf --- /dev/null +++ b/resources/spirv_source2c.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/Templates.rc b/source/Templates.rc index e32aca5..45ec7ef 100644 Binary files a/source/Templates.rc and b/source/Templates.rc differ diff --git a/source/configGenerator.cpp b/source/configGenerator.cpp index 2849443..a4ca4ea 100644 --- a/source/configGenerator.cpp +++ b/source/configGenerator.cpp @@ -367,6 +367,8 @@ bool ConfigGenerator::changeConfig(const string& option) outputLine(" --dce-only do not output a project and only generate missing DCE files"); outputLine( " --use-yasm use YASM instead of the default NASM (this is not advised as it does not support newer instructions)"); + outputLine( + " --tesseract-name=NAME set the base name for libtesseract (e.g. tesseract55) [tesseract]"); // Add in reserved values vector reservedItems; buildReservedValues(reservedItems); @@ -450,6 +452,18 @@ bool ConfigGenerator::changeConfig(const string& option) } else if (option == "--use-yasm") { // This has no parameters and just sets internal value m_useNASM = false; + } else if (option.find("--tesseract-name") == 0) { + // Check for correct command syntax + if (option.at(16) != '=') { + outputError("Incorrect tesseract-name syntax (" + option + ")"); + outputError("Expected syntax (--tesseract-name=NAME)", false); + return false; + } + m_tesseractName = option.substr(17); + if (m_tesseractName.empty()) { + outputError("Empty tesseract name specified"); + return false; + } } else if (option.find("--use-existing-config") == 0) { // A input config file has been specified m_usingExistingConfig = true; diff --git a/source/configGenerator_build.cpp b/source/configGenerator_build.cpp index 70edf5a..2cdde7d 100644 --- a/source/configGenerator_build.cpp +++ b/source/configGenerator_build.cpp @@ -427,9 +427,22 @@ bool ConfigGenerator::buildAutoDetectValues() enable = true; } } - } else if (i == "libglslang" || i == " libshaderc" || i == "spirv_compiler") { + } else if (i == "libglslang") { // Not currently supported enable = false; + } else if (i == "libshaderc") { + enable = false; + if (findEnvironmentVariable("VULKAN_SDK")) { + enable = true; + } else { + makeFileGeneratorRelative(m_outDirectory + "include/shaderc/shaderc.h", sFileName); + enable = findFile(sFileName, sFileName); + } + } else if (i == "spirv_compiler") { + enable = isConfigOptionEnabled("libshaderc") || isConfigOptionEnabled("libglslang"); + } else if (i == "libplacebo") { + makeFileGeneratorRelative(m_outDirectory + "include/libplacebo/config.h", sFileName); + enable = findFile(sFileName, sFileName); } else if (i == "w32threads") { enable = true; } else if (i == "xlib") { @@ -1142,7 +1155,8 @@ void ConfigGenerator::buildAdditionalDependencies(DependencyList& additionalDepe additionalDependencies["NV_ENC_PIC_PARAMS_AV1"] = bNvenc; const auto spirv = getConfigOption("spirv_compiler"); if ((spirv == m_configValues.end())) { - additionalDependencies["spirv_compiler"] = false; + additionalDependencies["spirv_compiler"] = + isConfigOptionEnabled("libshaderc") || isConfigOptionEnabled("libglslang"); } } diff --git a/source/projectGenerator.cpp b/source/projectGenerator.cpp index e633f6f..59a3cd7 100644 --- a/source/projectGenerator.cpp +++ b/source/projectGenerator.cpp @@ -35,6 +35,18 @@ #define TEMPLATE_PROPS_WINRT_ID 109 #define TEMPLATE_FILE_PROPS_ID 110 #define TEMPLATE_SLN_NOWINRT_ID 111 +#define BIN2C_EXE_ID 112 +#define SOURCE2C_EXE_ID 113 +#define OPENCL_SOURCE2C_PROPS_ID 114 +#define OPENCL_SOURCE2C_TARGETS_ID 115 +#define OPENCL_SOURCE2C_XML_ID 116 +#define CUDA_BIN2C_PROPS_ID 117 +#define CUDA_BIN2C_TARGETS_ID 118 +#define CUDA_BIN2C_XML_ID 119 +#define TEMPLATE_COMMON_PROPS_ID 120 +#define SPIRV_SOURCE2C_PROPS_ID 121 +#define SPIRV_SOURCE2C_TARGETS_ID 122 +#define SPIRV_SOURCE2C_XML_ID 123 bool ProjectGenerator::passAllMake() { @@ -76,6 +88,16 @@ bool ProjectGenerator::passAllMake() return false; } + // Copy common props file to output directory + string commonPropsFile; + if (!loadFromResourceFile(TEMPLATE_COMMON_PROPS_ID, commonPropsFile)) { + return false; + } + outPropsFile = m_configHelper.m_solutionDirectory + "smp_common.props"; + if (!writeToFile(outPropsFile, commonPropsFile, true)) { + return false; + } + // Loop through each library make file vector libraries; m_configHelper.getConfigList("LIBRARY_LIST", libraries); @@ -200,6 +222,75 @@ void ProjectGenerator::errorFunc(const bool cleanupFiles) exit(1); } +void ProjectGenerator::sanitizeSourceFiles() +{ + if (!m_configHelper.isConfigOptionEnabled("libplacebo")) { + return; + } + for (const auto& file : m_includesC) { + if (file.find("vf_libplacebo.c") == string::npos) { + continue; + } + string fullPath = file; + m_configHelper.makeFileGeneratorRelative(fullPath, fullPath); + string contents; + if (!loadFromFile(fullPath, contents, false, false)) { + continue; + } + if (contents.find("#if PL_API_VER") == string::npos) { + continue; + } + // Split into lines + vector lines; + size_t pos = 0; + while (pos < contents.size()) { + size_t eol = contents.find('\n', pos); + if (eol == string::npos) eol = contents.size(); + lines.push_back(contents.substr(pos, eol - pos)); + pos = eol + 1; + } + // Track parenthesis nesting; comment out #if PL_API_VER / #endif inside macro args + int parenDepth = 0; + int skipEndifCount = 0; + bool modified = false; + for (auto& line : lines) { + string trimmed = line; + size_t firstNonWS = trimmed.find_first_not_of(" \t\r"); + if (firstNonWS != string::npos) trimmed = trimmed.substr(firstNonWS); + if (parenDepth > 0 && trimmed.find("#if PL_API_VER") == 0 && + line.find("////") == string::npos) { + line = "////" + line; + skipEndifCount++; + modified = true; + } else if (skipEndifCount > 0 && (trimmed == "#endif" || trimmed == "#endif\r") && + line.find("////") == string::npos) { + line = "////" + line; + skipEndifCount--; + modified = true; + } else { + if (trimmed.empty() || trimmed[0] != '#') { + bool inString = false; + for (char c : line) { + if (c == '"') inString = !inString; + if (!inString) { + if (c == '(') parenDepth++; + else if (c == ')') parenDepth--; + } + } + } + } + } + if (modified) { + string result; + for (size_t i = 0; i < lines.size(); i++) { + result += lines[i]; + if (i + 1 < lines.size()) result += '\n'; + } + writeToFile(fullPath, result); + } + } +} + bool ProjectGenerator::outputProject() { // Check all files are correctly located @@ -215,6 +306,9 @@ bool ProjectGenerator::outputProject() StaticList definesStatic; buildDependencyValues(includeDirs, lib32Dirs, lib64Dirs, definesShared, definesStatic, false); + // Comment out #if/#endif inside macro arguments that MSVC cannot handle + sanitizeSourceFiles(); + // Create missing definitions of functions removed by DCE if (!outputProjectDCE(includeDirs)) { return false; @@ -291,6 +385,14 @@ bool ProjectGenerator::outputProject() outputCUDATools(projectFile); outputCUDATools(projectFileWinRT); + // Add OpenCL requirements + outputOpenCLTools(projectFile); + outputOpenCLTools(projectFileWinRT); + + // Add SPIRV requirements + outputSPIRVTools(projectFile); + outputSPIRVTools(projectFileWinRT); + // Add the dependency libraries if (!outputDependencyLibs(projectFile, false, false)) { return false; @@ -393,6 +495,12 @@ bool ProjectGenerator::outputProgramProject(const string& destinationFile, const // Add CUDA requirements outputCUDATools(programFile); + // Add OpenCL requirements + outputOpenCLTools(programFile); + + // Add SPIRV requirements + outputSPIRVTools(programFile); + // Add the dependency libraries if (!outputDependencyLibs(programFile, false, true)) { return false; @@ -1065,16 +1173,16 @@ void ProjectGenerator::outputSourceFiles(string& projectTemplate, string& filter // Output CUDA files if (!m_includesCU.empty()) { if (m_configHelper.isCUDAEnabled()) { - // outputSourceFileType( - // m_includesCU, "CudaCompile", "Source", projectTemplate, filterTemplate, foundObjects, foundFilters, - // true); - /*for (auto& i : m_includesConditionalCU) { + outputSourceFileType( + m_includesCU, "CudaBin2C", "Source", projectTemplate, filterTemplate, foundObjects, foundFilters, + false); + for (auto& i : m_includesConditionalCU) { fileList.clear(); fileList.emplace_back(i.first); - outputSourceFileType(fileList, "CudaCompile", "Source", projectTemplate, filterTemplate, foundObjects, - foundFilters, true, i.second.isStatic, i.second.isShared, i.second.is32, i.second.is64); - }*/ - outputError("CUDA files detected in project. CUDA compilation is not currently supported"); + outputSourceFileType(fileList, "CudaBin2C", "Source", projectTemplate, filterTemplate, + foundObjects, foundFilters, false, i.second.isStatic, i.second.isShared, i.second.is32, + i.second.is64); + } } else { outputError("CUDA files found in project but CUDA is disabled"); } @@ -1083,9 +1191,16 @@ void ProjectGenerator::outputSourceFiles(string& projectTemplate, string& filter // Output CL files if (!m_includesCL.empty()) { if (m_configHelper.isOpenCLEnabled()) { - // TODO: Embed source file as c file (ffmpeg uses tools/source2c for this) - outputError( - "OpenCL shader files detected in project. OpenCL shader compilation is not currently supported"); + outputSourceFileType( + m_includesCL, "OpenCLSource2C", "Source", projectTemplate, filterTemplate, foundObjects, foundFilters, + false); + for (auto& i : m_includesConditionalCL) { + fileList.clear(); + fileList.emplace_back(i.first); + outputSourceFileType(fileList, "OpenCLSource2C", "Source", projectTemplate, filterTemplate, + foundObjects, foundFilters, false, i.second.isStatic, i.second.isShared, i.second.is32, + i.second.is64); + } } else { outputError("OpenCL shader files found in project but opencl is disabled"); } @@ -1094,13 +1209,55 @@ void ProjectGenerator::outputSourceFiles(string& projectTemplate, string& filter // Output COMP files if (!m_includesCOMP.empty()) { if (m_configHelper.isSPIRVEnabled()) { - // TODO: Embed source file as c file (ffmpeg uses tools/source2c for this) - outputError("SPIRV shader files detected in project. Compute shader compilation is not currently supported"); + outputSourceFileType( + m_includesCOMP, "SPIRVSource2C", "Source", projectTemplate, filterTemplate, foundObjects, foundFilters, + false); + for (auto& i : m_includesConditionalCOMP) { + fileList.clear(); + fileList.emplace_back(i.first); + outputSourceFileType(fileList, "SPIRVSource2C", "Source", projectTemplate, filterTemplate, + foundObjects, foundFilters, false, i.second.isStatic, i.second.isShared, i.second.is32, + i.second.is64); + } } else { outputError("SPIRV shader files found in project but spirv is disabled"); } } + // Process resource files (HTML/CSS) from OBJS-resman + StaticList resourceFiles; + auto resmanIt = m_unknowns.find("OBJS-resman"); + if (resmanIt != m_unknowns.end()) { + for (const auto& resFile : resmanIt->second) { + // Convert .o extension back to source file + string sourceFile = resFile; + if (sourceFile.length() > 2 && sourceFile.substr(sourceFile.length() - 2) == ".o") { + sourceFile = sourceFile.substr(0, sourceFile.length() - 2); + // Check if this is a resource file we can process + if (sourceFile.find(".html") != string::npos || sourceFile.find(".css") != string::npos) { + resourceFiles.push_back(sourceFile); + + // Copy bin2c.exe if needed for resource processing + static bool bin2cCopied = false; + if (!bin2cCopied) { + string bin2cContent; + if (loadFromResourceFile(BIN2C_EXE_ID, bin2cContent)) { + string bin2cPath = m_configHelper.m_solutionDirectory + "bin2c.exe"; + if (writeToFile(bin2cPath, bin2cContent, true)) { + bin2cCopied = true; + } + } + } + } + } + } + + // Process resource files if any were found + if (!resourceFiles.empty()) { + outputResourceSourceFiles(resourceFiles, projectTemplate, filterTemplate, foundObjects, foundFilters); + } + } + // Output header files in new item group outputSourceFileType( m_includesH, "ClInclude", "Header", projectTemplate, filterTemplate, foundObjects, foundFilters, false); @@ -1647,10 +1804,422 @@ void ProjectGenerator::outputASMTools(string& projectTemplate) const } } +void ProjectGenerator::outputResourceSourceFiles(StaticList& fileList, string& projectTemplate, string& filterTemplate, + StaticList& foundObjects, set& foundFilters, bool staticOnly, bool sharedOnly, bool bit32Only, bool bit64Only) const +{ + // Constants for resource build + const string itemGroup = "\r\n "; + const string itemGroupEnd = "\r\n "; + const string includeClose = "\">"; + const string includeEnd = "\" />"; + const string typeInclude = "\r\n true"; + const string buildConfigsStatic[] = {"Release", "Debug", "ReleaseWinRT", "DebugWinRT"}; + const string buildConfigsShared[] = {"ReleaseDLL", "ReleaseDLLStaticDeps", "DebugDLL", "ReleaseDLLWinRT", + "ReleaseDLLWinRTStaticDeps", "DebugDLLWinRT"}; + + if (fileList.size() > 0) { + string resourceFiles = itemGroup; + string resourceFilesFilt = itemGroup; + string resourceFilesTemp, resourceFilesFiltTemp; + + for (const auto& i : fileList) { + // Resource custom build entry + resourceFilesTemp = typeInclude; + resourceFilesFiltTemp = typeInclude; + + // Add the fileName with full project-relative path + string file = i; + string fullPath; + if (findSourceFile(file.substr(0, file.rfind('.')), file.substr(file.rfind('.')), fullPath)) { + m_configHelper.makeFileProjectRelative(fullPath, file); + } + replace(file.begin(), file.end(), '/', '\\'); + resourceFilesTemp += file; + resourceFilesFiltTemp += file; + + // Get resource name without path or extension for generated .c file + uint pos = i.rfind('/') + 1; + string resourceName = i.substr(pos); + uint pos2 = resourceName.rfind('.'); + string extension = resourceName.substr(pos2); + resourceName.resize(pos2); + string outputCFile = "$(IntDir)\\" + resourceName + extension + ".c"; + + // Add the filters Filter + string sourceDir; + m_configHelper.makeFileProjectRelative(m_configHelper.m_rootDirectory, sourceDir); + pos = i.rfind(sourceDir); + pos = (pos == string::npos) ? 0 : pos + sourceDir.length(); + resourceFilesFiltTemp += includeClose; + resourceFilesFiltTemp += filterSource; + uint folderLength = i.rfind('/') - pos; + if (static_cast(folderLength) != -1) { + string folderName = file.substr(pos, folderLength); + folderName = '\\' + folderName; + foundFilters.insert("Source Files" + folderName); + resourceFilesFiltTemp += folderName; + } + resourceFilesFiltTemp += filterEnd; + resourceFilesFiltTemp += typeIncludeEnd; + + // Add resource compilation commands + resourceFilesTemp += includeClose; + + if (extension == ".css") { + // CSS processing: minify then convert to C array + string minFile = "$(IntDir)\\" + resourceName + ".css.min"; + string varName = resourceName; + replace(varName.begin(), varName.end(), '.', '_'); + varName += "_css"; + + resourceFilesTemp += "\r\n powershell -Command \"(Get-Content '%(FullPath)' -Raw) -replace '/\\*.*?\\*/', '' -replace '\\r?\\n', ' ' -replace '\\s+', ' ' -replace '^\\s+|\\s+$', '' | Out-File '" + minFile + "' -NoNewline -Encoding ASCII\" && \"$(ProjectDir)bin2c.exe\" \"" + minFile + "\" \"" + outputCFile + "\" " + varName + ""; + } else if (extension == ".html") { + // HTML processing: direct conversion to C array + string varName = resourceName; + replace(varName.begin(), varName.end(), '.', '_'); + varName += "_html"; + + resourceFilesTemp += "\r\n \"$(ProjectDir)bin2c.exe\" \"%(FullPath)\" \"" + outputCFile + "\" " + varName + ""; + } else { + // Generic resource processing + string varName = resourceName; + replace(varName.begin(), varName.end(), '.', '_'); + varName += "_" + extension.substr(1); // Remove the dot from extension + + resourceFilesTemp += "\r\n \"$(ProjectDir)bin2c.exe\" \"%(FullPath)\" \"" + outputCFile + "\" " + varName + ""; + } + + resourceFilesTemp += "\r\n " + outputCFile + ""; + resourceFilesTemp += "\r\n Converting resource file %(Filename)%(Extension)"; + + // Check if this file should be disabled under certain configurations + if (staticOnly || sharedOnly) { + const string* buildConfig = nullptr; + uint configs = 0; + if (staticOnly) { + buildConfig = buildConfigsShared; + configs = sizeof(buildConfigsShared) / sizeof(buildConfigsShared[0]); + } else { + buildConfig = buildConfigsStatic; + configs = sizeof(buildConfigsStatic) / sizeof(buildConfigsStatic[0]); + } + for (uint j = 0; j < configs; j++) { + resourceFilesTemp += excludeConfig; + resourceFilesTemp += buildConfig[j]; + resourceFilesTemp += excludeConfigEnd; + } + } else if (bit32Only || bit64Only) { + resourceFilesTemp += excludeConfigPlatform; + if (bit32Only) { + resourceFilesTemp += "x64"; + } else { + resourceFilesTemp += "Win32"; + } + resourceFilesTemp += excludeConfigEnd; + } + + resourceFilesTemp += typeIncludeEnd; + + // Add to output + resourceFiles += resourceFilesTemp; + resourceFilesFilt += resourceFilesFiltTemp; + } + + resourceFiles += itemGroupEnd; + resourceFilesFilt += itemGroupEnd; + + // Add generated .c files for compilation with explicit dependencies + string cFiles = itemGroup; + string cFilesFilt = itemGroup; + for (const auto& i : fileList) { + uint pos = i.rfind('/') + 1; + string resourceName = i.substr(pos); + uint pos2 = resourceName.rfind('.'); + string extension = resourceName.substr(pos2); + resourceName.resize(pos2); + string outputCFile = "$(IntDir)\\" + resourceName + extension + ".c"; + string origResourceFile = i; + replace(origResourceFile.begin(), origResourceFile.end(), '/', '\\'); + + // ClCompile entry with explicit dependency + cFiles += "\r\n "; + cFiles += "\r\n " + origResourceFile + ""; + + // Check if this file should be disabled under certain configurations + if (staticOnly || sharedOnly) { + const string* buildConfig = nullptr; + uint configs = 0; + if (staticOnly) { + buildConfig = buildConfigsShared; + configs = sizeof(buildConfigsShared) / sizeof(buildConfigsShared[0]); + } else { + buildConfig = buildConfigsStatic; + configs = sizeof(buildConfigsStatic) / sizeof(buildConfigsStatic[0]); + } + for (uint j = 0; j < configs; j++) { + cFiles += "\r\n " + "true"; + } + } else if (bit32Only || bit64Only) { + cFiles += "\r\n " + string("true"); + } + + cFiles += "\r\n "; + + // Filter entry for generated .c file + cFilesFilt += "\r\n "; + cFilesFilt += "\r\n Source Files\\Generated"; + cFilesFilt += "\r\n "; + } + cFiles += itemGroupEnd; + cFilesFilt += itemGroupEnd; + + // Add the Generated filter + foundFilters.insert("Source Files\\Generated"); + + // After add the item groups for resource files + string endTag = ""; + uint findPos = projectTemplate.rfind(endTag); + findPos += endTag.length(); + uint findPosFilt = filterTemplate.rfind(endTag); + findPosFilt += endTag.length(); + + // Insert into output file + projectTemplate.insert(findPos, resourceFiles + cFiles); + filterTemplate.insert(findPosFilt, resourceFilesFilt + cFilesFilt); + } +} + void ProjectGenerator::outputCUDATools(string& projectTemplate) const { if (m_configHelper.isCUDAEnabled() && (m_includesCU.size() > 0)) { - // TODO: Add cuda tools + // Copy bin2c.exe to project directory for CUDA compilation + string bin2cContent; + if (!loadFromResourceFile(BIN2C_EXE_ID, bin2cContent)) { + outputError("Failed to load bin2c.exe from resources"); + return; + } + string bin2cPath = m_configHelper.m_solutionDirectory + "bin2c.exe"; + if (!writeToFile(bin2cPath, bin2cContent, true)) { + outputError("Failed to copy bin2c.exe to project directory"); + return; + } + + // Copy cuda_bin2c.props to project directory + string propsContent; + if (!loadFromResourceFile(CUDA_BIN2C_PROPS_ID, propsContent)) { + outputError("Failed to load cuda_bin2c.props from resources"); + return; + } + string propsPath = m_configHelper.m_solutionDirectory + "cuda_bin2c.props"; + if (!writeToFile(propsPath, propsContent, true)) { + outputError("Failed to copy cuda_bin2c.props to project directory"); + return; + } + + // Copy cuda_bin2c.targets to project directory + string targetsContent; + if (!loadFromResourceFile(CUDA_BIN2C_TARGETS_ID, targetsContent)) { + outputError("Failed to load cuda_bin2c.targets from resources"); + return; + } + string targetsPath = m_configHelper.m_solutionDirectory + "cuda_bin2c.targets"; + if (!writeToFile(targetsPath, targetsContent, true)) { + outputError("Failed to copy cuda_bin2c.targets to project directory"); + return; + } + + // Copy cuda_bin2c.xml to project directory + string xmlContent; + if (!loadFromResourceFile(CUDA_BIN2C_XML_ID, xmlContent)) { + outputError("Failed to load cuda_bin2c.xml from resources"); + return; + } + string xmlPath = m_configHelper.m_solutionDirectory + "cuda_bin2c.xml"; + if (!writeToFile(xmlPath, xmlContent, true)) { + outputError("Failed to copy cuda_bin2c.xml to project directory"); + return; + } + + // Add CUDA build customisation imports + string propsCUDA = "\r\n\ + \r\n\ + \r\n\ + "; + string targetsCUDA = "\r\n\ + \r\n\ + \r\n\ + "; + + const string findProps = R"()"; + const string findTargets = R"()"; + + // Add cuda props import (after first ) + uint findPos = projectTemplate.find(findProps) + findProps.length(); + projectTemplate.insert(findPos, propsCUDA); + // Add cuda targets import (after last ) + findPos = projectTemplate.rfind(findTargets) + findTargets.length(); + projectTemplate.insert(findPos, targetsCUDA); + } +} + +void ProjectGenerator::outputOpenCLTools(string& projectTemplate) const +{ + if (m_configHelper.isOpenCLEnabled() && (m_includesCL.size() > 0)) { + // Copy source2c.exe to project directory + string source2cContent; + if (!loadFromResourceFile(SOURCE2C_EXE_ID, source2cContent)) { + outputError("Failed to load source2c.exe from resources"); + return; + } + string source2cPath = m_configHelper.m_solutionDirectory + "source2c.exe"; + if (!writeToFile(source2cPath, source2cContent, true)) { + outputError("Failed to copy source2c.exe to project directory"); + return; + } + + // Copy opencl_source2c.props to project directory + string propsContent; + if (!loadFromResourceFile(OPENCL_SOURCE2C_PROPS_ID, propsContent)) { + outputError("Failed to load opencl_source2c.props from resources"); + return; + } + string propsPath = m_configHelper.m_solutionDirectory + "opencl_source2c.props"; + if (!writeToFile(propsPath, propsContent, true)) { + outputError("Failed to copy opencl_source2c.props to project directory"); + return; + } + + // Copy opencl_source2c.targets to project directory + string targetsContent; + if (!loadFromResourceFile(OPENCL_SOURCE2C_TARGETS_ID, targetsContent)) { + outputError("Failed to load opencl_source2c.targets from resources"); + return; + } + string targetsPath = m_configHelper.m_solutionDirectory + "opencl_source2c.targets"; + if (!writeToFile(targetsPath, targetsContent, true)) { + outputError("Failed to copy opencl_source2c.targets to project directory"); + return; + } + + // Copy opencl_source2c.xml to project directory + string xmlContent; + if (!loadFromResourceFile(OPENCL_SOURCE2C_XML_ID, xmlContent)) { + outputError("Failed to load opencl_source2c.xml from resources"); + return; + } + string xmlPath = m_configHelper.m_solutionDirectory + "opencl_source2c.xml"; + if (!writeToFile(xmlPath, xmlContent, true)) { + outputError("Failed to copy opencl_source2c.xml to project directory"); + return; + } + + // Add OpenCL build customisation imports + string propsOpenCL = "\r\n\ + \r\n\ + \r\n\ + "; + string targetsOpenCL = "\r\n\ + \r\n\ + \r\n\ + "; + + const string findProps = R"()"; + const string findTargets = R"()"; + + // Add opencl props import (after first ) + uint findPos = projectTemplate.find(findProps) + findProps.length(); + projectTemplate.insert(findPos, propsOpenCL); + // Add opencl targets import (after last ) + findPos = projectTemplate.rfind(findTargets) + findTargets.length(); + projectTemplate.insert(findPos, targetsOpenCL); + } +} + +void ProjectGenerator::outputSPIRVTools(string& projectTemplate) const +{ + if (m_configHelper.isSPIRVEnabled() && (m_includesCOMP.size() > 0)) { + // Copy source2c.exe to project directory (may already exist from OpenCL) + string source2cPath = m_configHelper.m_solutionDirectory + "source2c.exe"; + string source2cCheck; + if (!findFile(source2cPath, source2cCheck)) { + string source2cContent; + if (!loadFromResourceFile(SOURCE2C_EXE_ID, source2cContent)) { + outputError("Failed to load source2c.exe from resources"); + return; + } + if (!writeToFile(source2cPath, source2cContent, true)) { + outputError("Failed to copy source2c.exe to project directory"); + return; + } + } + + // Copy spirv_source2c.props to project directory + string propsContent; + if (!loadFromResourceFile(SPIRV_SOURCE2C_PROPS_ID, propsContent)) { + outputError("Failed to load spirv_source2c.props from resources"); + return; + } + string propsPath = m_configHelper.m_solutionDirectory + "spirv_source2c.props"; + if (!writeToFile(propsPath, propsContent, true)) { + outputError("Failed to copy spirv_source2c.props to project directory"); + return; + } + + // Copy spirv_source2c.targets to project directory + string targetsContent; + if (!loadFromResourceFile(SPIRV_SOURCE2C_TARGETS_ID, targetsContent)) { + outputError("Failed to load spirv_source2c.targets from resources"); + return; + } + string targetsPath = m_configHelper.m_solutionDirectory + "spirv_source2c.targets"; + if (!writeToFile(targetsPath, targetsContent, true)) { + outputError("Failed to copy spirv_source2c.targets to project directory"); + return; + } + + // Copy spirv_source2c.xml to project directory + string xmlContent; + if (!loadFromResourceFile(SPIRV_SOURCE2C_XML_ID, xmlContent)) { + outputError("Failed to load spirv_source2c.xml from resources"); + return; + } + string xmlPath = m_configHelper.m_solutionDirectory + "spirv_source2c.xml"; + if (!writeToFile(xmlPath, xmlContent, true)) { + outputError("Failed to copy spirv_source2c.xml to project directory"); + return; + } + + // Add SPIRV build customisation imports + string propsSPIRV = "\r\n\ + \r\n\ + \r\n\ + "; + string targetsSPIRV = "\r\n\ + \r\n\ + \r\n\ + "; + + const string findProps = R"()"; + const string findTargets = R"()"; + + // Add spirv props import (after first ) + uint findPos = projectTemplate.find(findProps) + findProps.length(); + projectTemplate.insert(findPos, propsSPIRV); + // Add spirv targets import (after last ) + findPos = projectTemplate.rfind(findTargets) + findTargets.length(); + projectTemplate.insert(findPos, targetsSPIRV); } } @@ -1670,7 +2239,7 @@ bool ProjectGenerator::outputDependencyLibs(string& projectTemplate, const bool StaticList addLibs, libs = m_libs; buildDependencies(libs, addLibs, winrt); - if ((libs.size() > 0) || (addLibs.size() > 0)) { + if ((libs.size() > 0) || (addLibs.size() > 0) || m_configHelper.isConfigOptionEnabled("libshaderc")) { // Create list of additional ffmpeg dependencies string addFFmpegLibs[4]; // debug, release, debugDll, releaseDll for (const auto& i : m_projectLibs[m_projectName]) { @@ -1696,10 +2265,28 @@ bool ProjectGenerator::outputDependencyLibs(string& projectTemplate, const bool addDeps[3] += (!winrt) ? ".lib;" : "_winrt.lib;"; } // Create List of additional external dependencies - string addExternDeps; + // Uses same 4-way split as addDeps: debug, release, debugDll, releaseDll + string addExternDeps[4]; for (const auto& i : addLibs) { - addExternDeps += i; - addExternDeps += ".lib;"; + addExternDeps[0] += i + ".lib;"; + addExternDeps[1] += i + ".lib;"; + addExternDeps[2] += i + ".lib;"; + addExternDeps[3] += i + ".lib;"; + } + // Vulkan SDK libs with separate static/DLL names and debug variants + if (m_configHelper.isConfigOptionEnabled("libshaderc") && + (m_projectName == "libavfilter")) { + addExternDeps[0] += "shaderc_combinedd.lib;"; + addExternDeps[1] += "shaderc_combined.lib;"; + addExternDeps[2] += "shaderc_sharedd.lib;"; + addExternDeps[3] += "shaderc_shared.lib;"; + } + // Add tesseract with custom name if specified via --tesseract-name + string addTesseractDebug, addTesseractRelease; + if (m_addCustomTesseract) { + m_addCustomTesseract = false; + addTesseractDebug = m_configHelper.m_tesseractName + "d.lib;"; + addTesseractRelease = m_configHelper.m_tesseractName + ".lib;"; } // Add to Additional Dependencies const string libLink2[2] = {"", ""}; @@ -1750,7 +2337,8 @@ bool ProjectGenerator::outputDependencyLibs(string& projectTemplate, const bool addIndex += 2; } addString += addDeps[addIndex]; - addString += addExternDeps; + addString += addExternDeps[addIndex]; + addString += (debugRelease == 0) ? addTesseractDebug : addTesseractRelease; addString += "%(AdditionalDependencies)"; projectTemplate.insert(findPos, addString); findPos += addString.length(); diff --git a/source/projectGenerator_build.cpp b/source/projectGenerator_build.cpp index 8fe60ef..abcfc73 100644 --- a/source/projectGenerator_build.cpp +++ b/source/projectGenerator_build.cpp @@ -73,6 +73,10 @@ void ProjectGenerator::buildDependencies(StaticList& libs, StaticList& addLibs, if (m_projectName == "libavformat" && !winrt) { addLibs.emplace_back("ws2_32"); // Add the additional required libs } + // libplacebo statically links into libavfilter which requires vulkan-1 at link time + if (m_configHelper.isConfigOptionEnabled("libplacebo") && !winrt) { + addLibs.emplace_back("vulkan-1"); + } // Determine only those dependencies that are valid for current project map projectDeps; @@ -207,6 +211,14 @@ void ProjectGenerator::buildDependencies(StaticList& libs, StaticList& addLibs, addLibs.emplace_back("Advapi32"); // Add the additional required libs } else if (i == "vulkan") { // Doesn't need any additional libs + } else if (i == "libshaderc") { + // Handled separately in outputDependencyLibs (different static/DLL lib names) + } else if (i == "spirv_compiler") { + // Meta-dependency satisfied by libshaderc or libglslang, no additional libs needed + } else if (i == "libplacebo") { + lib = "libplacebo"; + } else if (i == "libtesseract" && m_configHelper.m_tesseractName != "tesseract") { + m_addCustomTesseract = true; } else { // By default just use the lib name and prefix with lib if not already if (i.find("lib") == 0) { @@ -283,6 +295,21 @@ void ProjectGenerator::buildDependencyValues(StaticList& includeDirs, StaticList includeDirs.push_back(projRoot + m_projectName + '/'); } + // libplacebo requires Vulkan SDK include and lib dirs for all projects that link against it + if (m_configHelper.isConfigOptionEnabled("libplacebo") && !winrt) { + definesStatic.emplace_back("PL_STATIC"); + definesShared.emplace_back("PL_STATIC"); + if (findEnvironmentVariable("VULKAN_SDK")) { + if (find(includeDirs.begin(), includeDirs.end(), "$(VULKAN_SDK)/include/") == includeDirs.end()) { + includeDirs.emplace_back("$(VULKAN_SDK)/include/"); + } + if (find(lib32Dirs.begin(), lib32Dirs.end(), "$(VULKAN_SDK)/Lib32") == lib32Dirs.end()) { + lib32Dirs.emplace_back("$(VULKAN_SDK)/Lib32"); + lib64Dirs.emplace_back("$(VULKAN_SDK)/Lib"); + } + } + } + // Determine only those dependencies that are valid for current project map projectDeps; buildProjectDependencies(projectDeps); @@ -396,7 +423,10 @@ void ProjectGenerator::buildDependencyValues(StaticList& includeDirs, StaticList } } else if (i.first == "vulkan" && !winrt) { if (findEnvironmentVariable("VULKAN_SDK")) { - includeDirs.emplace_back("$(VULKAN_SDK)/include/"); + if (find(includeDirs.begin(), includeDirs.end(), "$(VULKAN_SDK)/include/") == + includeDirs.end()) { + includeDirs.emplace_back("$(VULKAN_SDK)/include/"); + } } else { string fileName; m_configHelper.makeFileGeneratorRelative( @@ -411,6 +441,27 @@ void ProjectGenerator::buildDependencyValues(StaticList& includeDirs, StaticList outputWarning("Vulkan requires the Vulkan headers to be available in the include path.", false); } } + } else if (i.first == "libshaderc" && !winrt) { + if (findEnvironmentVariable("VULKAN_SDK")) { + if (find(includeDirs.begin(), includeDirs.end(), "$(VULKAN_SDK)/include/") == + includeDirs.end()) { + includeDirs.emplace_back("$(VULKAN_SDK)/include/"); + } + lib32Dirs.emplace_back("$(VULKAN_SDK)/Lib32"); + lib64Dirs.emplace_back("$(VULKAN_SDK)/Lib"); + } else { + outputWarning("Could not find the Vulkan SDK environment variable."); + outputWarning( + "Either the Vulkan SDK is not installed or the environment variable is missing.", false); + outputWarning("libshaderc requires the Vulkan SDK to be installed.", false); + } + } else if (i.first == "libplacebo" && !winrt) { + includeDirs.emplace_back("$(OutBaseDir)/include/"); + includeDirs.emplace_back("$(ProjectDir)/../../prebuilt/include/"); + if (findEnvironmentVariable("VULKAN_SDK")) { + lib32Dirs.emplace_back("$(VULKAN_SDK)/Lib32"); + lib64Dirs.emplace_back("$(VULKAN_SDK)/Lib"); + } } } } @@ -539,6 +590,9 @@ void ProjectGenerator::buildProjectDependencies(map& projectDeps) projectDeps["libzimg"] = (m_projectName == "libavfilter"); projectDeps["libzmq"] = (m_projectName == "libavfilter") || (m_projectName == "libavformat"); projectDeps["libzvbi"] = (m_projectName == "libavcodec"); + projectDeps["libshaderc"] = (m_projectName == "libavfilter"); + projectDeps["libplacebo"] = (m_projectName == "libavfilter"); + projectDeps["spirv_compiler"] = (m_projectName == "libavfilter"); projectDeps["lzma"] = (m_projectName == "libavcodec"); projectDeps["mediafoundation"] = (m_projectName == "libavcodec"); projectDeps["nvdec"] = (m_projectName == "libavcodec"); diff --git a/source/projectGenerator_compiler.cpp b/source/projectGenerator_compiler.cpp index 319b0f8..c4ac6ae 100644 --- a/source/projectGenerator_compiler.cpp +++ b/source/projectGenerator_compiler.cpp @@ -207,14 +207,14 @@ popd\n"; } } } - launchBat += "cl.exe "; - launchBat += extraCl + extraExtraCl + + string clCommand = "cl.exe "; + clCommand += extraCl + extraExtraCl + R"( /D"_DEBUG" /D"WIN32" /D"_WINDOWS" /D"HAVE_AV_CONFIG_H" /D"_USE_MATH_DEFINES" /D"_UCRT_NOISY_NAN" )" + runCommands + " /c /MP /w /nologo /utf-8"; for (const auto& file : compileFiles) { - launchBat += " \"" + file + "\""; + clCommand += " \"" + file + "\""; } - launchBat += " > ffvs_log.txt 2>&1\nif %errorlevel% neq 0 goto exitFail\n"; + launchBat += clCommand + " > ffvs_log.txt 2>&1\nif %errorlevel% neq 0 goto exitFail\n"; } if (runType == 1) { launchBat += "move *.i " + dirName + "/ >nul 2>&1\n"; diff --git a/source/projectGenerator_pass.cpp b/source/projectGenerator_pass.cpp index 0a5c365..83ea1e2 100644 --- a/source/projectGenerator_pass.cpp +++ b/source/projectGenerator_pass.cpp @@ -525,6 +525,17 @@ bool ProjectGenerator::passMake() return false; } } + } else if (m_inLine.substr(0, 11) == "OBJS-resman") { + // Find position after "+=" + uint startPos = m_inLine.find("+="); + if (startPos != string::npos) { + startPos += 2; // Skip past "+=" + // Found resource manager objects - store for later resolution + if (!passStaticInclude(startPos, m_unknowns["OBJS-resman"])) { + m_inputFile.close(); + return false; + } + } } else if (m_inLine.substr(0, 7) == "HEADERS") { // Found some headers if (m_inLine.at(7) == '-') { @@ -681,7 +692,7 @@ bool ProjectGenerator::passMake() } makeFiles.push_back(newMake); // Add to internal list of known subdirectories - const uint rootPos = newMake.find(m_configHelper.m_rootDirectory); + const uint rootPos = newMake.find(m_configHelper.m_rootDirectory); if (rootPos != string::npos) { newMake.erase(rootPos, m_configHelper.m_rootDirectory.length()); } @@ -720,6 +731,8 @@ bool ProjectGenerator::passMake() bool ProjectGenerator::passProgramMake() { uint checks = 2; + vector makeFiles; // Track additional includes + while (checks >= 1) { // Open the input Makefile string makeFile = m_projectDir + "MakeFile"; @@ -810,9 +823,133 @@ bool ProjectGenerator::passProgramMake() return false; } } + } else if (m_inLine.substr(0, 11) == "OBJS-resman") { + // Find position after "+=" + uint startPos = m_inLine.find("+="); + if (startPos != string::npos) { + startPos += 2; // Skip past "+=" + // Found resource manager objects - store for later resolution + if (!passStaticInclude(startPos, m_unknowns["OBJS-resman"])) { + m_inputFile.close(); + return false; + } + } + } else if (m_inLine.substr(0, 7) == "include" || m_inLine.substr(0, 8) == "-include") { + // Need to append the included file to makefile list + uint startPos = m_inLine.find_first_not_of(" \t", 7 + (m_inLine[0] == '-' ? 1 : 0)); + uint endPos = m_inLine.find_first_of(" \t\n\r", startPos + 1); + endPos = (endPos == string::npos) ? endPos : endPos - startPos; + string newMake = m_inLine.substr(startPos, endPos); + + // Check if this include contains function parameters (like $(1), $(2), etc.) and skip it + if (newMake.find("$(1)") != string::npos || newMake.find("$(2)") != string::npos || + newMake.find("$(3)") != string::npos || newMake.find("$(prog)") != string::npos || + newMake.find("$(P)") != string::npos) { + continue; // Skip this include as it's part of a Make function definition + } + + // Perform token substitution + startPos = newMake.find('$'); + while (startPos != string::npos) { + endPos = newMake.find(')', startPos + 1); + if (endPos == string::npos) { + outputError("Invalid token in include (" + newMake + ")"); + return false; + } + ++endPos; + string token = newMake.substr(startPos, endPos - startPos); + if (token == "$(SRC_PATH)") { + newMake.replace(startPos, endPos - startPos, m_configHelper.m_rootDirectory); + } else if (token == "$(ARCH)") { + newMake.replace(startPos, endPos - startPos, "x86"); + } else { + outputError("Unknown token in include (" + token + ")"); + return false; + } + startPos = newMake.find('$', startPos); + } + + makeFiles.push_back(newMake); } } m_inputFile.close(); + + // Process any included makefiles + while (!makeFiles.empty()) { + const string includedMakeFile = makeFiles.back(); + makeFiles.pop_back(); + outputLine(" Generating from Makefile (" + includedMakeFile + ")..."); + + // Open the included Makefile + ifstream includedFile(includedMakeFile); + if (includedFile.is_open()) { + string includedLine; + while (getline(includedFile, includedLine)) { + // Process OBJS-resman lines in included files + if (includedLine.length() >= 11 && includedLine.substr(0, 11) == "OBJS-resman") { + // Only process OBJS-resman for ffmpeg project + if (m_projectName == "ffmpeg") { + // Handle multi-line declarations directly here instead of using passStaticInclude + // to avoid file context mismatch + string fullLine = includedLine; + + // Read continuation lines from the included file + while (fullLine.back() == '\\') { + string nextLine; + if (getline(includedFile, nextLine)) { + fullLine += " " + nextLine; // Append with space + } else { + break; // No more lines + } + } + + // Now parse the complete line manually + uint startPos = fullLine.find("+="); + if (startPos != string::npos) { + startPos += 2; // Skip past "+=" + + // Parse files from the line + uint pos = fullLine.find_first_not_of("+=: \t", startPos); + while (pos != string::npos && pos < fullLine.length()) { + // Skip backslashes and whitespace + if (fullLine[pos] == '\\' || fullLine[pos] == ' ' || fullLine[pos] == '\t') { + pos = fullLine.find_first_not_of(" \t\\", pos + 1); + continue; + } + + // Find end of current file + uint endPos = fullLine.find_first_of(" \t\\", pos); + if (endPos == string::npos) { + endPos = fullLine.length(); + } + + string file = fullLine.substr(pos, endPos - pos); + if (!file.empty() && file != "+=" && file != "OBJS-resman") { + // Special handling for resman.o - convert to resman since it's a real source file + if (file == "fftools/resources/resman.o") { + string sourceFile = "fftools/resources/resman"; + m_includes.push_back(sourceFile); + outputInfo("Found Static: '" + sourceFile + ".c' (converted from resman.o)"); + } + + // Store in unknowns for $(OBJS-resman) resolution + // These are object files that will be resolved when $(OBJS-resman) is encountered + m_unknowns["OBJS-resman"].push_back(file); + outputInfo("Found Static: '" + file + "' (stored for $(OBJS-resman) resolution)"); + } + + pos = fullLine.find_first_not_of(" \t\\", endPos); + } + } + } + } + } + includedFile.close(); + } else { + outputInfo("Could not open included MakeFile (" + includedMakeFile + ")"); + } + } + if (checks == 2) { string ignored; const string makeFolder = "fftools/"; @@ -829,9 +966,15 @@ bool ProjectGenerator::passProgramMake() uint uiPos; for (auto i = m_includes.begin(); i < m_includes.end(); ++i) { if ((uiPos = i->find(makeFolder)) != string::npos) { - i->erase(uiPos, makeFolder.length()); + // Don't strip fftools/ prefix from resman as it's a real source file in that location + if (*i != "fftools/resources/resman") { + i->erase(uiPos, makeFolder.length()); + } else { + // Keep full path for resman.c + } } } + --checks; } diff --git a/templates/smp_common.props b/templates/smp_common.props new file mode 100644 index 0000000..ab5869a --- /dev/null +++ b/templates/smp_common.props @@ -0,0 +1,53 @@ + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + + + %(AdditionalLibraryDirectories) + + + diff --git a/templates/template_in.vcxproj b/templates/template_in.vcxproj index 9ca51d8..6952789 100644 --- a/templates/template_in.vcxproj +++ b/templates/template_in.vcxproj @@ -6,6 +6,7 @@ + diff --git a/templates/templateprogram_in.vcxproj b/templates/templateprogram_in.vcxproj index 4889347..83eae1f 100644 --- a/templates/templateprogram_in.vcxproj +++ b/templates/templateprogram_in.vcxproj @@ -98,6 +98,9 @@ + + +