|
|
|
@ -9,19 +9,71 @@
|
|
|
|
#include <AzCore/IO/Path/Path.h>
|
|
|
|
#include <AzCore/IO/Path/Path.h>
|
|
|
|
#include <AzCore/IO/SystemFile.h>
|
|
|
|
#include <AzCore/IO/SystemFile.h>
|
|
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
|
|
|
|
|
|
#include <AzCore/Console/IConsole.h>
|
|
|
|
#include <AzCore/std/containers/array.h>
|
|
|
|
#include <AzCore/std/containers/array.h>
|
|
|
|
#include <AzCore/std/tuple.h>
|
|
|
|
#include <AzCore/std/tuple.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AZ_CVAR(bool, ap_tether_lifetime, true, nullptr, AZ::ConsoleFunctorFlags::Null,
|
|
|
|
|
|
|
|
"If enabled, a parent process that launches the AP will terminate the AP on exit");
|
|
|
|
|
|
|
|
|
|
|
|
namespace AzFramework::AssetSystem::Platform
|
|
|
|
namespace AzFramework::AssetSystem::Platform
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void AllowAssetProcessorToForeground()
|
|
|
|
void AllowAssetProcessorToForeground()
|
|
|
|
{}
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[noreturn]] static void LaunchAssetProcessorDirectly(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AZStd::fixed_vector<const char*, 5> args {
|
|
|
|
|
|
|
|
assetProcessorPath.c_str(),
|
|
|
|
|
|
|
|
"--start-hidden",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the engine path to the launch command if not empty
|
|
|
|
|
|
|
|
AZ::IO::FixedMaxPathString engineRootArg;
|
|
|
|
|
|
|
|
if (!engineRoot.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// No need to quote these paths, this code calls exec directly and
|
|
|
|
|
|
|
|
// does not go through shell string interpolation
|
|
|
|
|
|
|
|
engineRootArg = AZ::IO::FixedMaxPathString{"--engine-path="} + AZ::IO::FixedMaxPathString{engineRoot};
|
|
|
|
|
|
|
|
args.push_back(engineRootArg.data());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the active project path to the launch command if not empty
|
|
|
|
|
|
|
|
AZ::IO::FixedMaxPathString projectPathArg;
|
|
|
|
|
|
|
|
if (!projectPath.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
projectPathArg = AZ::IO::FixedMaxPathString{"--regset=/Amazon/AzCore/Bootstrap/project_path="} + AZ::IO::FixedMaxPathString{projectPath};
|
|
|
|
|
|
|
|
args.push_back(projectPathArg.data());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure this is at the end
|
|
|
|
|
|
|
|
args.push_back(nullptr); // argv itself needs to be null-terminated
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
execv(args[0], const_cast<char**>(args.data()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// exec* family of functions only return on error
|
|
|
|
|
|
|
|
fprintf(stderr, "Asset Processor failed with error: %s\n", strerror(errno));
|
|
|
|
|
|
|
|
_exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static pid_t LaunchAssetProcessorDaemonized(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// detach the child from parent
|
|
|
|
|
|
|
|
setsid();
|
|
|
|
|
|
|
|
const pid_t secondChildPid = fork();
|
|
|
|
|
|
|
|
if (secondChildPid == 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return secondChildPid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LaunchAssetProcessor(AZStd::string_view executableDirectory, AZStd::string_view engineRoot,
|
|
|
|
bool LaunchAssetProcessor(AZStd::string_view executableDirectory, AZStd::string_view engineRoot,
|
|
|
|
AZStd::string_view projectPath)
|
|
|
|
AZStd::string_view projectPath)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -40,7 +92,8 @@ namespace AzFramework::AssetSystem::Platform
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t firstChildPid = fork();
|
|
|
|
const pid_t parentPid = getpid();
|
|
|
|
|
|
|
|
const pid_t firstChildPid = fork();
|
|
|
|
if (firstChildPid == 0)
|
|
|
|
if (firstChildPid == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// redirect output to dev/null so it doesn't hijack an existing console window
|
|
|
|
// redirect output to dev/null so it doesn't hijack an existing console window
|
|
|
|
@ -53,51 +106,33 @@ namespace AzFramework::AssetSystem::Platform
|
|
|
|
AZ::IO::FileDescriptorRedirector stderrRedirect(STDERR_FILENO);
|
|
|
|
AZ::IO::FileDescriptorRedirector stderrRedirect(STDERR_FILENO);
|
|
|
|
stderrRedirect.RedirectTo(devNull, mode);
|
|
|
|
stderrRedirect.RedirectTo(devNull, mode);
|
|
|
|
|
|
|
|
|
|
|
|
// detach the child from parent
|
|
|
|
if (ap_tether_lifetime)
|
|
|
|
setsid();
|
|
|
|
|
|
|
|
pid_t secondChildPid = fork();
|
|
|
|
|
|
|
|
if (secondChildPid == 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZStd::array args {
|
|
|
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
|
|
assetProcessorPath.c_str(), assetProcessorPath.c_str(), "--start-hidden",
|
|
|
|
if (getppid() != parentPid)
|
|
|
|
static_cast<const char*>(nullptr), static_cast<const char*>(nullptr), static_cast<const char*>(nullptr)
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
int optionalArgPos = 3;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the engine path to the launch command if not empty
|
|
|
|
|
|
|
|
AZ::IO::FixedMaxPathString engineRootArg;
|
|
|
|
|
|
|
|
if (!engineRoot.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
engineRootArg = AZ::IO::FixedMaxPathString::format(R"(--engine-path="%.*s")",
|
|
|
|
|
|
|
|
aznumeric_cast<int>(engineRoot.size()), engineRoot.data());
|
|
|
|
|
|
|
|
args[optionalArgPos++] = engineRootArg.data();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the active project path to the launch command if not empty
|
|
|
|
|
|
|
|
AZ::IO::FixedMaxPathString projectPathArg;
|
|
|
|
|
|
|
|
if (!projectPath.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
projectPathArg = AZ::IO::FixedMaxPathString::format(R"(--regset="/Amazon/AzCore/Bootstrap/project_path=%.*s")",
|
|
|
|
_exit(1);
|
|
|
|
aznumeric_cast<int>(projectPath.size()), projectPath.data());
|
|
|
|
|
|
|
|
args[optionalArgPos++] = projectPathArg.data();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath);
|
|
|
|
AZStd::apply(execl, args);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// exec* family of functions only exit on error
|
|
|
|
|
|
|
|
AZ_Error("AssetSystemComponent", false, "Asset Processor failed with error: %s", strerror(errno));
|
|
|
|
|
|
|
|
_exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const pid_t secondChildPid = LaunchAssetProcessorDaemonized(assetProcessorPath, engineRoot, projectPath);
|
|
|
|
|
|
|
|
stdoutRedirect.Reset();
|
|
|
|
|
|
|
|
stderrRedirect.Reset();
|
|
|
|
|
|
|
|
|
|
|
|
stdoutRedirect.Reset();
|
|
|
|
// exit the transient child with proper return code
|
|
|
|
stderrRedirect.Reset();
|
|
|
|
int ret = (secondChildPid < 0) ? 1 : 0;
|
|
|
|
|
|
|
|
_exit(ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// exit the transient child with proper return code
|
|
|
|
|
|
|
|
int ret = (secondChildPid < 0) ? 1 : 0;
|
|
|
|
|
|
|
|
_exit(ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (firstChildPid > 0)
|
|
|
|
else if (firstChildPid > 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ap_tether_lifetime)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
// wait for first child to exit to ensure the second child was started
|
|
|
|
// wait for first child to exit to ensure the second child was started
|
|
|
|
int status = 0;
|
|
|
|
int status = 0;
|
|
|
|
pid_t ret = waitpid(firstChildPid, &status, 0);
|
|
|
|
pid_t ret = waitpid(firstChildPid, &status, 0);
|
|
|
|
@ -106,4 +141,4 @@ namespace AzFramework::AssetSystem::Platform
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace AzFramework::AssetSystem::Platform
|
|
|
|
|