mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
CMake: Add embedded Linux subprocess for WebView support
In order to display a WebKit based webview a plugin will deploy a temporary standalone executable on the system and host the WebKit instance inside that.
This commit is contained in:
parent
e4a86316ca
commit
f9ff497978
6 changed files with 204 additions and 31 deletions
|
|
@ -160,6 +160,7 @@ install(FILES "${JUCE_BINARY_DIR}/JUCEConfigVersion.cmake"
|
|||
"${JUCE_CMAKE_UTILS_DIR}/checkBundleSigning.cmake"
|
||||
"${JUCE_CMAKE_UTILS_DIR}/copyDir.cmake"
|
||||
"${JUCE_CMAKE_UTILS_DIR}/juce_runtime_arch_detection.cpp"
|
||||
"${JUCE_CMAKE_UTILS_DIR}/juce_LinuxSubprocessHelper.cpp"
|
||||
DESTINATION "${JUCE_INSTALL_DESTINATION}")
|
||||
|
||||
if("${CMAKE_SOURCE_DIR}" STREQUAL "${JUCE_SOURCE_DIR}")
|
||||
|
|
@ -170,4 +171,3 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${JUCE_SOURCE_DIR}")
|
|||
install(EXPORT LV2_HELPER NAMESPACE juce:: DESTINATION "${JUCE_INSTALL_DESTINATION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -764,6 +764,21 @@ This function sets the `CMAKE_<LANG>_FLAGS_<MODE>` to empty in the current direc
|
|||
allowing alternative optimisation/debug flags to be supplied without conflicting with the
|
||||
CMake-supplied defaults.
|
||||
|
||||
#### `juce_link_with_embedded_linux_subprocess`
|
||||
|
||||
juce_link_with_embedded_linux_subprocess(<target>)
|
||||
|
||||
This function links the provided target with an interface library that generates a barebones
|
||||
standalone executable file and embeds it as a binary resource. This binary resource is only used
|
||||
by the `juce_gui_extra` module and only when its `JUCE_WEB_BROWSER` capability is enabled. This
|
||||
executable will then be deployed into a temporary file only when the code is running in a
|
||||
non-standalone format, and will be used to host a WebKit view. This technique is used by audio
|
||||
plugins on Linux.
|
||||
|
||||
This function is automatically called if necessary for all targets created by one of the JUCE target
|
||||
creation functions i.e. `juce_add_gui_app`, `juce_add_console_app` and `juce_add_gui_app`. You don't
|
||||
need to call this function manually in these cases.
|
||||
|
||||
### Targets
|
||||
|
||||
#### `juce::juce_recommended_warning_flags`
|
||||
|
|
|
|||
|
|
@ -147,6 +147,86 @@ endfunction()
|
|||
|
||||
# ==================================================================================================
|
||||
|
||||
function(_juce_create_linux_subprocess_helper_target)
|
||||
if(TARGET juce_linux_subprocess_helper)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(source "${JUCE_CMAKE_UTILS_DIR}/juce_LinuxSubprocessHelper.cpp")
|
||||
|
||||
add_executable(juce_linux_subprocess_helper ${source})
|
||||
add_executable(juce::juce_linux_subprocess_helper ALIAS juce_linux_subprocess_helper)
|
||||
target_compile_features(juce_linux_subprocess_helper PRIVATE cxx_std_17)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(juce_linux_subprocess_helper PRIVATE Threads::Threads ${CMAKE_DL_LIBS})
|
||||
endfunction()
|
||||
|
||||
function(_juce_create_embedded_linux_subprocess_target output_target_name target)
|
||||
# This library needs to be created in every directory where a target wants to link against it.
|
||||
# Pre CMake 3.20 the GENERATED property is only visible in the directory of the current target,
|
||||
# and even post 3.20, CMake will not know how to generate the sources outside this directory.
|
||||
set(target_directory_key JUCE_EMBEDDED_LINUX_SUBPROCESS_TARGET)
|
||||
get_directory_property(embedded_linux_subprocess_target ${target_directory_key})
|
||||
|
||||
if(embedded_linux_subprocess_target)
|
||||
set(${output_target_name} juce::${embedded_linux_subprocess_target} PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(prefix "_juce_directory_prefix")
|
||||
set(embedded_linux_subprocess_target ${prefix}_embedded_linux_subprocess)
|
||||
|
||||
while(TARGET ${embedded_linux_subprocess_target})
|
||||
set(embedded_linux_subprocess_target ${prefix}_${embedded_linux_subprocess_target})
|
||||
endwhile()
|
||||
|
||||
_juce_create_linux_subprocess_helper_target()
|
||||
|
||||
get_target_property(generated_sources_directory ${target} JUCE_GENERATED_SOURCES_DIRECTORY)
|
||||
|
||||
if(generated_sources_directory)
|
||||
set(juce_linux_subprocess_helper_binary_dir "${generated_sources_directory}")
|
||||
else()
|
||||
set(juce_linux_subprocess_helper_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/juce_LinuxSubprocessHelper")
|
||||
endif()
|
||||
|
||||
set(binary_header_file "${juce_linux_subprocess_helper_binary_dir}/juce_LinuxSubprocessHelperBinaryData.h")
|
||||
set(binary_source_file "${juce_linux_subprocess_helper_binary_dir}/juce_LinuxSubprocessHelperBinaryData.cpp")
|
||||
set(juceaide_input_file "${juce_linux_subprocess_helper_binary_dir}/input_file_list")
|
||||
|
||||
file(GENERATE OUTPUT ${juceaide_input_file} CONTENT "$<TARGET_FILE:juce_linux_subprocess_helper>")
|
||||
file(MAKE_DIRECTORY ${juce_linux_subprocess_helper_binary_dir})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${binary_header_file}
|
||||
${binary_source_file}
|
||||
COMMAND juce::juceaide binarydata "LinuxSubprocessHelperBinaryData" "${binary_header_file}"
|
||||
${juce_linux_subprocess_helper_binary_dir} "${juceaide_input_file}"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E rename "${juce_linux_subprocess_helper_binary_dir}/BinaryData1.cpp" "${binary_source_file}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
DEPENDS juce_linux_subprocess_helper
|
||||
VERBATIM)
|
||||
|
||||
add_library(${embedded_linux_subprocess_target} INTERFACE)
|
||||
target_sources(${embedded_linux_subprocess_target} INTERFACE ${binary_source_file})
|
||||
target_include_directories(${embedded_linux_subprocess_target} INTERFACE ${juce_linux_subprocess_helper_binary_dir})
|
||||
target_compile_definitions(${embedded_linux_subprocess_target} INTERFACE JUCE_USE_EXTERNAL_TEMPORARY_SUBPROCESS=1)
|
||||
add_library(juce::${embedded_linux_subprocess_target} ALIAS ${embedded_linux_subprocess_target})
|
||||
|
||||
set_directory_properties(PROPERTIES ${target_directory_key} ${embedded_linux_subprocess_target})
|
||||
set(${output_target_name} juce::${embedded_linux_subprocess_target} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(juce_link_with_embedded_linux_subprocess target)
|
||||
_juce_create_embedded_linux_subprocess_target(embedded_linux_subprocess_target ${target})
|
||||
target_link_libraries(${target} PRIVATE ${embedded_linux_subprocess_target})
|
||||
endfunction()
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
# Ideally, we'd check the preprocessor defs on the target to see whether
|
||||
# JUCE_USE_CURL, JUCE_WEB_BROWSER, or JUCE_IN_APP_PURCHASES have been explicitly turned off,
|
||||
# and then link libraries as appropriate.
|
||||
|
|
@ -167,6 +247,12 @@ function(_juce_link_optional_libraries target)
|
|||
|
||||
if(needs_browser)
|
||||
target_link_libraries(${target} PRIVATE juce::pkgconfig_JUCE_BROWSER_LINUX_DEPS)
|
||||
|
||||
get_target_property(is_plugin ${target} JUCE_IS_PLUGIN)
|
||||
|
||||
if(is_plugin)
|
||||
juce_link_with_embedded_linux_subprocess(${target})
|
||||
endif()
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
get_target_property(needs_storekit ${target} JUCE_NEEDS_STORE_KIT)
|
||||
|
|
@ -846,10 +932,9 @@ function(_juce_add_lv2_manifest_helper_target)
|
|||
add_executable(juce::juce_lv2_helper ALIAS juce_lv2_helper)
|
||||
target_compile_features(juce_lv2_helper PRIVATE cxx_std_17)
|
||||
set_target_properties(juce_lv2_helper PROPERTIES BUILD_WITH_INSTALL_RPATH ON)
|
||||
target_link_libraries(juce_lv2_helper PRIVATE ${CMAKE_DL_LIBS})
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(juce_lv2_helper PRIVATE Threads::Threads)
|
||||
target_link_libraries(juce_lv2_helper PRIVATE Threads::Threads ${CMAKE_DL_LIBS})
|
||||
endfunction()
|
||||
|
||||
# ==================================================================================================
|
||||
|
|
|
|||
36
extras/Build/CMake/juce_LinuxSubprocessHelper.cpp
Normal file
36
extras/Build/CMake/juce_LinuxSubprocessHelper.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
int main (int argc, const char* const* argv)
|
||||
{
|
||||
if (argc >= 3)
|
||||
if (auto* handle = dlopen (argv[1], RTLD_LAZY))
|
||||
if (auto* function = reinterpret_cast<int (*) (int, const char* const*)> (dlsym (handle, argv[2])))
|
||||
return function (argc - 3, argv + 3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray()
|
|||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
|
||||
extern int juce_gtkWebkitMain (int argc, const char* argv[]);
|
||||
extern "C" int juce_gtkWebkitMain (int argc, const char* const* argv);
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
|
|
@ -231,7 +231,7 @@ int JUCEApplicationBase::main (int argc, const char* argv[])
|
|||
initialiseNSApplication();
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined (JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
|
||||
if (argc >= 2 && String (argv[1]) == "--juce-gtkwebkitfork-child")
|
||||
return juce_gtkWebkitMain (argc, argv);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_USE_EXTERNAL_TEMPORARY_SUBPROCESS
|
||||
#include "juce_LinuxSubprocessHelperBinaryData.h"
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
|
|
@ -215,7 +219,7 @@ private:
|
|||
JUCE_IMPLEMENT_SINGLETON (WebKitSymbols)
|
||||
|
||||
//==============================================================================
|
||||
extern int juce_gtkWebkitMain (int argc, const char* argv[]);
|
||||
extern "C" int juce_gtkWebkitMain (int argc, const char* const* argv);
|
||||
|
||||
class CommandReceiver
|
||||
{
|
||||
|
|
@ -776,34 +780,68 @@ private:
|
|||
ret = pipe (outPipe);
|
||||
jassert (ret == 0);
|
||||
|
||||
std::vector<String> arguments;
|
||||
|
||||
#if JUCE_USE_EXTERNAL_TEMPORARY_SUBPROCESS
|
||||
if (! JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
subprocessFile.emplace ("_juce_linux_subprocess");
|
||||
|
||||
const auto externalSubprocessAvailable = subprocessFile->getFile().replaceWithData (LinuxSubprocessHelperBinaryData::juce_linux_subprocess_helper,
|
||||
LinuxSubprocessHelperBinaryData::juce_linux_subprocess_helperSize)
|
||||
&& subprocessFile->getFile().setExecutePermission (true);
|
||||
|
||||
ignoreUnused (externalSubprocessAvailable);
|
||||
jassert (externalSubprocessAvailable);
|
||||
|
||||
/* The external subprocess will load the .so specified as its first argument and execute
|
||||
the function specified by the second. The remaining arguments will be passed on to
|
||||
the function.
|
||||
*/
|
||||
arguments.emplace_back (subprocessFile->getFile().getFullPathName());
|
||||
arguments.emplace_back (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
|
||||
arguments.emplace_back ("juce_gtkWebkitMain");
|
||||
}
|
||||
#endif
|
||||
|
||||
arguments.emplace_back (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
|
||||
arguments.emplace_back ("--juce-gtkwebkitfork-child");
|
||||
arguments.emplace_back (outPipe[0]);
|
||||
arguments.emplace_back (inPipe [1]);
|
||||
|
||||
if (userAgent.isNotEmpty())
|
||||
arguments.emplace_back (userAgent);
|
||||
|
||||
std::vector<const char*> argv (arguments.size() + 1, nullptr);
|
||||
std::transform (arguments.begin(), arguments.end(), argv.begin(), [] (const auto& arg)
|
||||
{
|
||||
return arg.toRawUTF8();
|
||||
});
|
||||
|
||||
auto pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
close (inPipe[0]);
|
||||
close (outPipe[1]);
|
||||
|
||||
StringArray arguments;
|
||||
|
||||
arguments.add (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
|
||||
arguments.add ("--juce-gtkwebkitfork-child");
|
||||
arguments.add (String (outPipe[0]));
|
||||
arguments.add (String (inPipe [1]));
|
||||
|
||||
if (userAgent.isNotEmpty())
|
||||
arguments.add (userAgent);
|
||||
|
||||
std::vector<const char*> argv;
|
||||
argv.reserve (static_cast<std::size_t> (arguments.size() + 1));
|
||||
|
||||
for (const auto& arg : arguments)
|
||||
argv.push_back (arg.toRawUTF8());
|
||||
|
||||
argv.push_back (nullptr);
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
execv (arguments[0].toRawUTF8(), (char**) argv.data());
|
||||
}
|
||||
else
|
||||
juce_gtkWebkitMain (arguments.size(), (const char**) argv.data());
|
||||
{
|
||||
#if JUCE_USE_EXTERNAL_TEMPORARY_SUBPROCESS
|
||||
execv (arguments[0].toRawUTF8(), (char**) argv.data());
|
||||
#else
|
||||
// After a fork in a multithreaded program, the child can only safely call
|
||||
// async-signal-safe functions until it calls execv, but if we reached this point
|
||||
// then execv won't be called at all! The following call is unsafe, and is very
|
||||
// likely to lead to unexpected behaviour.
|
||||
jassertfalse;
|
||||
juce_gtkWebkitMain ((int) arguments.size(), argv.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
|
@ -881,11 +919,9 @@ private:
|
|||
|
||||
void handleCommand (const String& cmd, const var& params) override
|
||||
{
|
||||
MessageManager::callAsync ([liveness = std::weak_ptr<int> (livenessProbe), this, cmd, params]()
|
||||
MessageManager::callAsync ([liveness = std::weak_ptr (livenessProbe), this, cmd, params]
|
||||
{
|
||||
if (liveness.lock() == nullptr)
|
||||
return;
|
||||
|
||||
if (liveness.lock() != nullptr)
|
||||
handleCommandOnMessageThread (cmd, params);
|
||||
});
|
||||
}
|
||||
|
|
@ -903,6 +939,7 @@ private:
|
|||
std::unique_ptr<XEmbedComponent> xembed;
|
||||
std::shared_ptr<int> livenessProbe = std::make_shared<int> (0);
|
||||
std::vector<pollfd> pfds;
|
||||
std::optional<TemporaryFile> subprocessFile;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -1015,7 +1052,7 @@ bool WebBrowserComponent::areOptionsSupported (const Options& options)
|
|||
return (options.getBackend() == Options::Backend::defaultBackend);
|
||||
}
|
||||
|
||||
int juce_gtkWebkitMain (int argc, const char* argv[])
|
||||
extern "C" __attribute__ ((visibility ("default"))) int juce_gtkWebkitMain (int argc, const char* const* argv)
|
||||
{
|
||||
if (argc < 4)
|
||||
return -1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue