Fix missing export files for extensions (#4773)

This commit is contained in:
D8H
2023-01-02 19:11:11 +01:00
committed by GitHub
parent 9b199d5aa0
commit c62f9c5c65
11 changed files with 129 additions and 251 deletions

View File

@@ -11,30 +11,36 @@
namespace gd {
std::set<gd::String> UsedExtensionsFinder::ScanProject(gd::Project& project) {
const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::WholeProjectRefactorer::ExposeProjectObjects(project, worker);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, worker);
return worker.usedExtensions;
return worker.result;
};
// Objects scanner
void UsedExtensionsFinder::DoVisitObject(gd::Object& object) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType())
.GetExtension()
.GetName());
void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
};
// Behaviors scanner
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior& behavior) {
usedExtensions.insert(
gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName())
.GetExtension()
.GetName());
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
for (auto &&includeFile : metadata.GetMetadata().requiredFiles) {
result.GetUsedRequiredFiles().insert(includeFile);
}
};
// Instructions scanner
@@ -46,7 +52,10 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
usedExtensions.insert(metadata.GetExtension().GetName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
}
size_t i = 0;
for (auto expression : instruction.GetParameters()) {
@@ -61,7 +70,7 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
rootType = "number";
expression.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
}
return false;
}
@@ -93,31 +102,35 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
// Add extensions bound to Objects/Behaviors/Functions
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(), GetGlobalObjectsContainer(), GetObjectsContainer(), rootType, node);
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetGlobalObjectsContainer(),
GetObjectsContainer(), rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName)
.GetExtension()
.GetName());
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
}
};
@@ -138,7 +151,10 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
usedExtensions.insert(metadata.GetExtension().GetName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
}
};
} // namespace gd

View File

@@ -21,18 +21,62 @@ class Behavior;
namespace gd {
class GD_CORE_API UsedExtensionsResult {
public:
/**
* The extensions used by the project (or part of it).
*/
const std::set<gd::String> &GetUsedExtensions() const {
return usedExtensions;
}
/**
* The include files used at runtime by the project (or part of it).
*/
const std::set<gd::String> &GetUsedIncludeFiles() const {
return usedIncludeFiles;
}
/**
* The additional files required at runtime by the project (or part of it).
*/
const std::set<gd::String> &GetUsedRequiredFiles() const {
return usedRequiredFiles;
}
/**
* The extensions used by the project (or part of it).
*/
std::set<gd::String> &GetUsedExtensions() { return usedExtensions; }
/**
* The include files used at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedIncludeFiles() { return usedIncludeFiles; }
/**
* The additional files required at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
};
class GD_CORE_API UsedExtensionsFinder
: public ArbitraryObjectsWorker,
public ArbitraryEventsWorkerWithContext,
public ExpressionParser2NodeWorker {
public:
static std::set<gd::String> ScanProject(gd::Project& project);
static const UsedExtensionsResult ScanProject(gd::Project& project);
private:
UsedExtensionsFinder(gd::Project& project_) : project(project_){};
gd::Project& project;
gd::String rootType;
std::set<gd::String> usedExtensions;
UsedExtensionsResult result;
// Object Visitor
void DoVisitObject(gd::Object& object) override;

View File

@@ -32,6 +32,11 @@
namespace gdjs {
static void InsertUnique(std::vector<gd::String> &container, gd::String str) {
if (std::find(container.begin(), container.end(), str) == container.end())
container.push_back(str);
}
Exporter::Exporter(gd::AbstractFileSystem &fileSystem, gd::String gdjsRoot_)
: fs(fileSystem), gdjsRoot(gdjsRoot_) {
SetCodeOutputDirectory(fs.GetTempDir() + "/GDTemporaries/JSCodeTemp");
@@ -52,10 +57,11 @@ bool Exporter::ExportWholePixiProject(
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
gd::Project exportedProject = project;
auto usedExtensions = gd::UsedExtensionsFinder::ScanProject(project);
auto usedExtensionsResult = gd::UsedExtensionsFinder::ScanProject(project);
auto& usedExtensions = usedExtensionsResult.GetUsedExtensions();
auto exportProject = [this, &exportedProject, &exportOptions, &helper](
gd::String exportDir) {
auto exportProject = [this, &exportedProject, &exportOptions, &helper,
&usedExtensionsResult](gd::String exportDir) {
bool exportForCordova = exportOptions["exportForCordova"];
bool exportForFacebookInstantGames =
exportOptions["exportForFacebookInstantGames"];
@@ -88,9 +94,14 @@ bool Exporter::ExportWholePixiProject(
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
helper.ExportObjectAndBehaviorsRequiredFiles(exportedProject, resourcesFiles);
// Export files for free function, object and behaviors
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
InsertUnique(includesFiles, includeFile);
}
for (const auto &requiredFile :
usedExtensionsResult.GetUsedRequiredFiles()) {
InsertUnique(resourcesFiles, requiredFile);
}
// Export effects (after engine libraries as they auto-register themselves
// to the engine)

View File

@@ -41,6 +41,7 @@
#include "GDCore/Tools/Log.h"
#include "GDJS/Events/CodeGeneration/LayoutCodeGenerator.h"
#include "GDJS/Extensions/JsPlatform.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
#undef CopyFile // Disable an annoying macro
namespace {
@@ -115,9 +116,15 @@ bool ExporterHelper::ExportProjectForPixiPreview(
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
ExportObjectAndBehaviorsIncludes(immutableProject, includesFiles);
ExportObjectAndBehaviorsRequiredFiles(immutableProject, resourcesFiles);
// Export files for free function, object and behaviors
auto usedExtensionsResult =
gd::UsedExtensionsFinder::ScanProject(exportedProject);
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
InsertUnique(includesFiles, includeFile);
}
for (const auto &requiredFile : usedExtensionsResult.GetUsedRequiredFiles()) {
InsertUnique(resourcesFiles, requiredFile);
}
// Export effects (after engine libraries as they auto-register themselves to
// the engine)
@@ -797,98 +804,6 @@ bool ExporterHelper::ExportIncludesAndLibs(
return true;
}
void ExporterHelper::ExportObjectAndBehaviorsIncludes(
const gd::Project &project, std::vector<gd::String> &includesFiles) {
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {
for (const auto &includeFile : newIncludeFiles) {
InsertUnique(includesFiles, includeFile);
}
};
auto addObjectIncludeFiles = [&](const gd::Object &object) {
// Ensure needed files are included for the object type and its behaviors.
const gd::ObjectMetadata &metadata =
gd::MetadataProvider::GetObjectMetadata(JsPlatform::Get(),
object.GetType());
addIncludeFiles(metadata.includeFiles);
std::vector<gd::String> behaviors = object.GetAllBehaviorNames();
for (std::size_t j = 0; j < behaviors.size(); ++j) {
const gd::BehaviorMetadata &metadata =
gd::MetadataProvider::GetBehaviorMetadata(
JsPlatform::Get(),
object.GetBehavior(behaviors[j]).GetTypeName());
addIncludeFiles(metadata.includeFiles);
}
};
auto addObjectsIncludeFiles =
[&](const gd::ObjectsContainer &objectsContainer) {
for (std::size_t i = 0; i < objectsContainer.GetObjectsCount(); ++i) {
addObjectIncludeFiles(objectsContainer.GetObject(i));
}
};
// TODO UsedExtensionsFinder should be used instead to find the file to include.
// The Exporter class already use it.
addObjectsIncludeFiles(project);
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
const gd::Layout &layout = project.GetLayout(i);
addObjectsIncludeFiles(layout);
}
// Event based objects children
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount(); e++) {
auto& eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto&& eventsBasedObjectUniquePtr :
eventsFunctionsExtension.GetEventsBasedObjects()
.GetInternalVector()) {
auto eventsBasedObject = eventsBasedObjectUniquePtr.get();
addObjectsIncludeFiles(*eventsBasedObject);
}
}
}
void ExporterHelper::ExportObjectAndBehaviorsRequiredFiles(
const gd::Project &project, std::vector<gd::String> &requiredFiles) {
auto addRequiredFiles = [&](const std::vector<gd::String> &newRequiredFiles) {
for (const auto &requiredFile : newRequiredFiles) {
InsertUnique(requiredFiles, requiredFile);
}
};
auto addObjectRequiredFiles = [&](const gd::Object &object) {
// Ensure needed files are included for the object type and its behaviors.
// TODO: Handle required files declared by objects. For now, no objects has
// a need for additional required files, so the object metadata do not even
// have `requiredFiles`.
std::vector<gd::String> behaviors = object.GetAllBehaviorNames();
for (std::size_t j = 0; j < behaviors.size(); ++j) {
const gd::BehaviorMetadata &metadata =
gd::MetadataProvider::GetBehaviorMetadata(
JsPlatform::Get(),
object.GetBehavior(behaviors[j]).GetTypeName());
addRequiredFiles(metadata.requiredFiles);
}
};
auto addObjectsRequiredFiles =
[&](const gd::ObjectsContainer &objectsContainer) {
for (std::size_t i = 0; i < objectsContainer.GetObjectsCount(); ++i) {
addObjectRequiredFiles(objectsContainer.GetObject(i));
}
};
addObjectsRequiredFiles(project);
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
const gd::Layout &layout = project.GetLayout(i);
addObjectsRequiredFiles(layout);
}
}
void ExporterHelper::ExportResources(gd::AbstractFileSystem &fs,
gd::Project &project,
gd::String exportDir) {

View File

@@ -247,19 +247,6 @@ class ExporterHelper {
bool ExportEffectIncludes(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Add the include files for all the objects of the project
* and their behaviors.
*/
void ExportObjectAndBehaviorsIncludes(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Add the required files for all the objects of the project
* and their behaviors.
*/
void ExportObjectAndBehaviorsRequiredFiles(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Copy the external source files used by the game into the export
* directory, and add them into files to be included.

View File

@@ -2221,8 +2221,12 @@ interface PropertyFunctionGenerator {
boolean STATIC_CanGenerateGetterAndSetter([Const, Ref] AbstractEventsBasedEntity eventsBasedBehavior, [Const, Ref] NamedPropertyDescriptor property);
};
interface UsedExtensionsResult {
[Const, Ref] SetString GetUsedExtensions();
};
interface UsedExtensionsFinder {
[Value] SetString STATIC_ScanProject([Ref] Project project);
[Value] UsedExtensionsResult STATIC_ScanProject([Ref] Project project);
};
interface ExtensionAndBehaviorMetadata {

View File

@@ -108,6 +108,7 @@ describe('libGD.js', function () {
it('should have a list of extensions', function () {
expect(
gd.UsedExtensionsFinder.scanProject(project)
.getUsedExtensions()
.toNewVectorString()
.toJSArray()
).toEqual([]);
@@ -116,6 +117,7 @@ describe('libGD.js', function () {
expect(
gd.UsedExtensionsFinder.scanProject(project)
.getUsedExtensions()
.toNewVectorString()
.toJSArray()
).toEqual(['Sprite']);

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdUsedExtensionsFinder {
static scanProject(project: gdProject): gdSetString;
static scanProject(project: gdProject): gdUsedExtensionsResult;
delete(): void;
ptr: number;
};

View File

@@ -0,0 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdUsedExtensionsResult {
getUsedExtensions(): gdSetString;
delete(): void;
ptr: number;
};

View File

@@ -152,6 +152,7 @@ declare class libGDevelop {
VectorUnfilledRequiredBehaviorPropertyProblem: Class<gdVectorUnfilledRequiredBehaviorPropertyProblem>;
WholeProjectRefactorer: Class<gdWholeProjectRefactorer>;
PropertyFunctionGenerator: Class<gdPropertyFunctionGenerator>;
UsedExtensionsResult: Class<gdUsedExtensionsResult>;
UsedExtensionsFinder: Class<gdUsedExtensionsFinder>;
ExtensionAndBehaviorMetadata: Class<gdExtensionAndBehaviorMetadata>;
ExtensionAndObjectMetadata: Class<gdExtensionAndObjectMetadata>;

View File

@@ -43,7 +43,6 @@ type Options = {|
type CodeGenerationContext = {|
codeNamespacePrefix: string,
extensionIncludeFiles: Array<string>,
|};
const mangleName = (name: string) => {
@@ -145,29 +144,6 @@ const loadProjectEventsFunctionsExtension = (
});
};
/**
* Get the list of mandatory include files when using the
* extension.
*/
const getExtensionIncludeFiles = (
project: gdProject,
eventsFunctionsExtension: gdEventsFunctionsExtension,
options: Options,
codeNamespacePrefix: string
): Array<string> => {
return mapFor(0, eventsFunctionsExtension.getEventsFunctionsCount(), i => {
const eventsFunction = eventsFunctionsExtension.getEventsFunctionAt(i);
const codeNamespace = getFreeFunctionCodeNamespace(
eventsFunction,
codeNamespacePrefix
);
const functionName = codeNamespace + '.func'; // TODO
return options.eventsFunctionCodeWriter.getIncludeFileFor(functionName);
}).filter(Boolean);
};
/**
* Generate the code for the events based extension
*/
@@ -182,15 +158,8 @@ const generateEventsFunctionExtension = (
const codeNamespacePrefix =
'gdjs.evtsExt__' + mangleName(eventsFunctionsExtension.getName());
const extensionIncludeFiles = getExtensionIncludeFiles(
project,
eventsFunctionsExtension,
options,
codeNamespacePrefix
);
const codeGenerationContext = {
codeNamespacePrefix,
extensionIncludeFiles,
};
return Promise.all(
@@ -246,9 +215,6 @@ const generateEventsFunctionExtension = (
)
)
.then(functionInfos => {
if (!options.skipCodeGeneration) {
applyFunctionIncludeFilesDependencyTransitivity(functionInfos);
}
return extension;
});
};
@@ -301,11 +267,6 @@ const generateFreeFunction = (
.setIncludeFile(functionFile)
.setFunctionName(functionName);
// Always include the extension include files when using a free function.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
instructionOrExpression.addIncludeFile(includeFile);
});
if (!options.skipCodeGeneration) {
const includeFiles = new gd.SetString();
const eventsFunctionsExtensionCodeGenerator = new gd.EventsFunctionsExtensionCodeGenerator(
@@ -353,65 +314,6 @@ const generateFreeFunction = (
}
};
/**
* Add dependencies between functions according to transitivity.
* @param functionInfos free function metadatas
*/
const applyFunctionIncludeFilesDependencyTransitivity = (
functionInfos: Array<{
functionFile: string,
functionMetadata:
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata,
}>
): void => {
// Note that the iteration order doesn't matter, for instance for:
// a -> b
// b -> c
// c -> d
//
// going from a to c:
// a -> (b -> c)
// b -> c
// c -> d
//
// or from c to a:
// a -> b
// b -> (c -> d)
// c -> d
//
// give the same result:
// a -> (b -> (c -> d))
// b -> (c -> d)
// c -> d
const includeFileSets = functionInfos.map(
functionInfo =>
new Set(functionInfo.functionMetadata.getIncludeFiles().toJSArray())
);
// For any function A of the extension...
for (let index = 0; index < functionInfos.length; index++) {
const includeFiles = includeFileSets[index];
const functionIncludeFile = functionInfos[index].functionFile;
// ...and any function B of the extension...
for (let otherIndex = 0; otherIndex < functionInfos.length; otherIndex++) {
const otherFunctionMetadata = functionInfos[otherIndex].functionMetadata;
const otherIncludeFileSet = includeFileSets[otherIndex];
// ...where function B depends on function A...
if (otherIncludeFileSet.has(functionIncludeFile)) {
// ...add function A dependencies to the function B ones.
includeFiles.forEach(includeFile => {
if (!otherIncludeFileSet.has(includeFile)) {
otherIncludeFileSet.add(includeFile);
otherFunctionMetadata.addIncludeFile(includeFile);
}
});
}
}
}
};
function generateBehavior(
project: gdProject,
extension: gdPlatformExtension,
@@ -436,11 +338,6 @@ function generateBehavior(
behaviorMetadata.setIncludeFile(includeFile);
// Always include the extension include files when using a behavior.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
behaviorMetadata.addIncludeFile(includeFile);
});
return Promise.resolve().then(() => {
const behaviorMethodMangledNames = new gd.MapStringString();
@@ -557,11 +454,6 @@ function generateObject(
objectMetadata.setIncludeFile(includeFile);
// Always include the extension include files when using an object.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
objectMetadata.addIncludeFile(includeFile);
});
return Promise.resolve().then(() => {
const objectMethodMangledNames = new gd.MapStringString();