From 8fa09570ec91a656e55f88de882fc81fe39f35fa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Sep 2023 22:19:22 -0700 Subject: Updates to build on mac. --- indra/cmake/00-Common.cmake | 2 + indra/cmake/WebRTC.cmake | 26 +++++-- indra/llwebrtc/CMakeLists.txt | 34 +++++---- indra/llwebrtc/llwebrtc_impl.h | 11 ++- indra/newview/llappviewer.h | 1 + indra/newview/llstartup.cpp | 2 +- indra/newview/llviewerregion.h | 1 + indra/newview/llvoicewebrtc.cpp | 21 ++---- indra/newview/llvoicewebrtc.h | 152 +++++++++++++++++++-------------------- indra/newview/viewer_manifest.py | 14 ++++ 10 files changed, 149 insertions(+), 115 deletions(-) (limited to 'indra') diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 24534c98d9..687ace431b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -182,6 +182,8 @@ if (LINUX OR DARWIN) list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor ) + list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable ) + add_compile_options(${GCC_WARNINGS}) add_compile_options(-m${ADDRESS_SIZE}) endif (LINUX OR DARWIN) diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake index 7953d1ee1b..2040c86b9f 100644 --- a/indra/cmake/WebRTC.cmake +++ b/indra/cmake/WebRTC.cmake @@ -26,16 +26,30 @@ FetchContent_MakeAvailable(webrtc) set(WEBRTC_PATH ${webrtc_SOURCE_DIR}) - add_library( ll::webrtc INTERFACE IMPORTED ) - if (WINDOWS) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.lib" ) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.lib" ) elseif (DARWIN) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) + FIND_LIBRARY(COREAUDIO_LIBRARY CoreAudio) + FIND_LIBRARY(COREGRAPHICS_LIBRARY CoreGraphics) + FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY AudioToolbox) + FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) + FIND_LIBRARY(COCOA_LIBRARY Cocoa) + + target_link_libraries( ll::webrtc INTERFACE + "${WEBRTC_PATH}/lib/libwebrtc.a" + ${COREAUDIO_LIBRARY} + ${AUDIOTOOLBOX_LIBRARY} + ${COREGRAPHICS_LIBRARY} + ${COREFOUNDATION_LIBRARY} + ${COCOA_LIBRARY} + ) elseif (LINUX) - target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/webrtc.a" ) + target_link_libraries( ll::webrtc INTERFACE "${WEBRTC_PATH}/lib/libwebrtc.a" ) endif (WINDOWS) -target_include_directories( ll::webrtc SYSTEM INTERFACE "${WEBRTC_PATH}/include" "${WEBRTC_PATH}/include/third_party/abseil-cpp") + +message("PATH: ${WEBRTC_PATH}/include") + +target_include_directories( ll::webrtc INTERFACE "${WEBRTC_PATH}/include" "${WEBRTC_PATH}/include/third_party/abseil-cpp") diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index 881a3af607..fde4b87a3e 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -13,11 +13,6 @@ include(WebRTC) project(llwebrtc) -message(STATUS "C Compiler executable: ${CMAKE_C_COMPILER}") -message(STATUS "CXX Compiler executable: ${CMAKE_CXX_COMPILER}") -message(STATUS "Linker executable: ${CMAKE_LINKER}") -message(STATUS "SharedLib: ${SHARED_LIB_STAGING_DIR}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") @@ -34,10 +29,11 @@ set(llwebrtc_HEADER_FILES list(APPEND llwebrtc_SOURCE_FILES ${llwebrtc_HEADER_FILES}) add_library (llwebrtc SHARED ${llwebrtc_SOURCE_FILES}) - + set_target_properties(llwebrtc PROPERTIES PUBLIC_HEADER llwebrtc.h) -target_link_libraries(llwebrtc PRIVATE ll::webrtc +if (WINDOWS) + target_link_libraries(llwebrtc PRIVATE ll::webrtc secur32 winmm dmoguids @@ -45,13 +41,23 @@ target_link_libraries(llwebrtc PRIVATE ll::webrtc msdmo strmiids iphlpapi) -target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -set_property(TARGET llwebrtc PROPERTY - MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") - -install(TARGETS llwebrtc RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/sharedlibs/RelWithDebInfo") - +elseif (DARWIN) + target_link_libraries(llwebrtc PRIVATE ll::webrtc) +elseif (LINUX) + target_link_libraries(llwebrtc PRIVATE ll::webrtc) +endif (WINDOWS) + +target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +if (WINDOWS) + set_property(TARGET llwebrtc PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") +endif (WINDOWS) + +ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + ${SHARED_LIB_STAGING_DIR}) # Add tests if (LL_TESTS) endif (LL_TESTS) diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 10916e5a25..07c0f514b2 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -28,12 +28,21 @@ #define LLWEBRTC_IMPL_H #define LL_MAKEDLL +#if defined(_WIN32) || defined(_WIN64) #define WEBRTC_WIN 1 +#elif defined(__APPLE__) +#define WEBRTC_MAC 1 +#define WEBRTC_POSIX 1 +#elif __linux__ +#define WEBRTC_LINUX 1 +#endif + #include "llwebrtc.h" // WebRTC Includes #ifdef WEBRTC_WIN #pragma warning(disable : 4996) #endif // WEBRTC_WIN + #include "api/scoped_refptr.h" #include "rtc_base/ref_count.h" #include "rtc_base/ref_counted_object.h" @@ -174,4 +183,4 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, } -#endif // LLWEBRTC_IMPL_H \ No newline at end of file +#endif // LLWEBRTC_IMPL_H diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 6d1496d517..bfa1dea324 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -43,6 +43,7 @@ #ifndef LL_LLAPPVIEWER_H #define LL_LLAPPVIEWER_H +#include "llapp.h" #include "llallocator.h" #include "llapr.h" #include "llcontrol.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index d0b76848f7..e86ea5f2e3 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3337,7 +3337,7 @@ LLSD transform_cert_args(LLPointer cert) // are actually arrays, and we want to format them as comma separated // strings, so special case those. LLSDSerialize::toXML(cert_info[iter->first], std::cout); - if((iter->first == std::string(CERT_KEY_USAGE)) || + if((iter->first== std::string(CERT_KEY_USAGE)) || (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) { value = ""; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index a409d837a4..b24ff51479 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -43,6 +43,7 @@ #include "m4math.h" // LLMatrix4 #include "llframetimer.h" #include "llreflectionmap.h" +#include "llpointer.h" // Surface id's #define LAND 1 diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6e68ca7e4f..5fcfc969b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -87,31 +87,19 @@ namespace { static const std::string VOICE_SERVER_TYPE = "WebRTC"; - // Don't retry connecting to the daemon more frequently than this: - const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; - const int DAEMON_CONNECT_RETRY_MAX = 3; - // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.5f; // Timeout for connection to WebRTC const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; const F32 CONNECT_DNS_TIMEOUT = 5.0f; - const int CONNECT_RETRY_MAX = 3; - const F32 LOGIN_ATTEMPT_TIMEOUT = 30.0f; const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; - const int LOGIN_RETRY_MAX = 3; - - const F32 PROVISION_RETRY_TIMEOUT = 2.0; - const int PROVISION_RETRY_MAX = 5; - + // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); - const F32 SESSION_JOIN_TIMEOUT = 30.0f; - // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process @@ -126,9 +114,6 @@ namespace { // Maximum length of capture buffer recordings in seconds. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; - - const int ERROR_WebRTC_OBJECT_NOT_FOUND = 1001; - const int ERROR_WebRTC_NOT_LOGGED_IN = 1007; } static int scale_mic_volume(float volume) @@ -829,7 +814,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) { - channelSDP = result["jsep"]["sdp"]; + channelSDP = result["jsep"]["sdp"].asString(); } std::string voiceAccountServerUri; std::string voiceUserName = gAgent.getID().asString(); @@ -1468,6 +1453,8 @@ bool LLWebRTCVoiceClient::waitForChannel() << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; return !sShuttingDown; + case VOICE_CHANNEL_STATE_CHECK_EFFECTS: + break; } } while (true); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 56ca74f6e1..22c022ffdb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -61,7 +61,7 @@ class LLWebRTCVoiceClient : public LLSingleton, public llwebrtc::LLWebRTCDevicesObserver, public llwebrtc::LLWebRTCSignalingObserver { - LLSINGLETON(LLWebRTCVoiceClient); + LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); virtual ~LLWebRTCVoiceClient(); @@ -69,26 +69,26 @@ public: /// @name LLVoiceModuleInterface virtual implementations /// @see LLVoiceModuleInterface //@{ - virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - virtual void terminate(); // Call this to clean up during shutdown + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown - virtual const LLVoiceVersionInfo& getVersion(); + const LLVoiceVersionInfo& getVersion() override; - virtual void updateSettings(); // call after loading settings and whenever they change + void updateSettings() override; // call after loading settings and whenever they change // Returns true if WebRTC has successfully logged in and is not in error state - virtual bool isVoiceWorking() const; + bool isVoiceWorking() const override; ///////////////////// /// @name Tuning //@{ - virtual void tuningStart(); - virtual void tuningStop(); - virtual bool inTuningMode(); + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; - virtual void tuningSetMicVolume(float volume); - virtual void tuningSetSpeakerVolume(float volume); - virtual float tuningGetEnergy(void); + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; //@} ///////////////////// @@ -96,40 +96,40 @@ public: //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. - virtual bool deviceSettingsAvailable(); - virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel. + bool deviceSettingsAvailable() override; + bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the WebRTC daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - virtual void refreshDeviceLists(bool clearCurrentList = true); + void refreshDeviceLists(bool clearCurrentList = true) override; - virtual void setCaptureDevice(const std::string& name); - virtual void setRenderDevice(const std::string& name); + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; - virtual LLVoiceDeviceList& getCaptureDevices(); - virtual LLVoiceDeviceList& getRenderDevices(); + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; //@} - virtual void getParticipantList(std::set &participants); - virtual bool isParticipant(const LLUUID& speaker_id); + void getParticipantList(std::set &participants) override; + bool isParticipant(const LLUUID& speaker_id) override; // Send a text message to the specified user, initiating the session if necessary. // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // close any existing text IM session with the specified user - virtual void endUserIMSession(const LLUUID &uuid); + void endUserIMSession(const LLUUID &uuid) override; // Returns true if calling back the session URI after the session has closed is possible. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + BOOL isSessionCallBackPossible(const LLUUID &session_id) override; // Returns true if the session can accepte text IM's. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + BOOL isSessionTextIMPossible(const LLUUID &session_id) override; //////////////////////////// @@ -137,21 +137,21 @@ public: //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. - virtual bool inProximalChannel(); + bool inProximalChannel() override; - virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials); + void setNonSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials); + bool setSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual void leaveNonSpatialChannel(); + void leaveNonSpatialChannel() override; - virtual void leaveChannel(void); + void leaveChannel(void) override; // Returns the URI of the current channel, or an empty string if not currently in a channel. // NOTE that it will return an empty string if it's in the process of joining a channel. - virtual std::string getCurrentChannel(); + std::string getCurrentChannel() override; //@} @@ -159,59 +159,59 @@ public: /// @name invitations //@{ // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid); - virtual bool isValidChannel(std::string &channelHandle); - virtual bool answerInvite(std::string &channelHandle); - virtual void declineInvite(std::string &channelHandle); + void callUser(const LLUUID &uuid) override; + bool isValidChannel(std::string &channelHandle) override; + bool answerInvite(std::string &channelHandle) override; + void declineInvite(std::string &channelHandle) override; //@} ///////////////////////// /// @name Volume/gain //@{ - virtual void setVoiceVolume(F32 volume); - virtual void setMicGain(F32 volume); + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled(); - virtual void setVoiceEnabled(bool enabled); - virtual BOOL lipSyncEnabled(); - virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + bool voiceEnabled() override; + void setVoiceEnabled(bool enabled) override; + BOOL lipSyncEnabled() override; + void setLipSyncEnabled(BOOL enabled) override; + void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} ////////////////////////// /// @name nearby speaker accessors //@{ - virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id); - virtual BOOL isParticipantAvatar(const LLUUID &id); - virtual BOOL getIsSpeaking(const LLUUID& id); - virtual BOOL getIsModeratorMuted(const LLUUID& id); - virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id); - virtual F32 getUserVolume(const LLUUID& id); - virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id) override; + BOOL isParticipantAvatar(const LLUUID &id) override; + BOOL getIsSpeaking(const LLUUID& id) override; + BOOL getIsModeratorMuted(const LLUUID& id) override; + F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id) override; + F32 getUserVolume(const LLUUID& id) override; + void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} // authorize the user - virtual void userAuthorized(const std::string& user_id, - const LLUUID &agentID); + void userAuthorized(const std::string& user_id, + const LLUUID &agentID) override; ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer); - virtual void removeObserver(LLVoiceClientStatusObserver* observer); - virtual void addObserver(LLFriendObserver* observer); - virtual void removeObserver(LLFriendObserver* observer); - virtual void addObserver(LLVoiceClientParticipantObserver* observer); - virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + void addObserver(LLVoiceClientStatusObserver* observer) override; + void removeObserver(LLVoiceClientStatusObserver* observer) override; + void addObserver(LLFriendObserver* observer) override; + void removeObserver(LLFriendObserver* observer) override; + void addObserver(LLVoiceClientParticipantObserver* observer) override; + void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - virtual std::string sipURIFromID(const LLUUID &id); + std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -221,20 +221,20 @@ public: ////////////////////////// /// @name Accessors //@{ - virtual bool setVoiceEffect(const LLUUID& id); - virtual const LLUUID getVoiceEffect(); - virtual LLSD getVoiceEffectProperties(const LLUUID& id); + bool setVoiceEffect(const LLUUID& id) override; + const LLUUID getVoiceEffect() override; + LLSD getVoiceEffectProperties(const LLUUID& id) override; - virtual void refreshVoiceEffectLists(bool clear_lists); - virtual const voice_effect_list_t& getVoiceEffectList() const; - virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + void refreshVoiceEffectLists(bool clear_lists) override; + const voice_effect_list_t& getVoiceEffectList() const override; + const voice_effect_list_t& getVoiceEffectTemplateList() const override; //@} ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceEffectObserver* observer); - virtual void removeObserver(LLVoiceEffectObserver* observer); + void addObserver(LLVoiceEffectObserver* observer) override; + void removeObserver(LLVoiceEffectObserver* observer) override; //@} ////////////////////////////// @@ -263,13 +263,13 @@ public: ////////////////////////////// /// @name Effect preview buffer //@{ - virtual void enablePreviewBuffer(bool enable); - virtual void recordPreviewBuffer(); - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); - virtual void stopPreviewBuffer(); + void enablePreviewBuffer(bool enable) override; + void recordPreviewBuffer() override; + void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; + void stopPreviewBuffer() override; - virtual bool isPreviewRecording(); - virtual bool isPreviewPlaying(); + bool isPreviewRecording() override; + bool isPreviewPlaying() override; //@} //@} @@ -773,7 +773,7 @@ private: std::string getAudioSessionURI(); std::string getAudioSessionHandle(); - void setHidden(bool hidden); //virtual + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); void sendFriendsListUpdates(); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 1fa4df1682..9230cb0589 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -547,6 +547,12 @@ class Windows_x86_64_Manifest(ViewerManifest): # Get shared libs from the shared libs staging directory with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', self.args['buildtype'])): + # WebRTC libraries + for libfile in ( + 'llwebrtc.dll', + ): + self.path(libfile) + # Get fmodstudio dll if needed if self.args['fmodstudio'] == 'ON': if(self.args['buildtype'].lower() == 'debug'): @@ -990,6 +996,14 @@ class Darwin_x86_64_Manifest(ViewerManifest): print("Skipping %s" % dst) return added + # WebRTC libraries + with self.prefix(src=os.path.join(self.args['build'], os.pardir, + 'sharedlibs', self.args['buildtype'], 'Resources')): + for libfile in ( + 'libllwebrtc.dylib', + ): + self.path(libfile) + # dylibs is a list of all the .dylib files we expect to need # in our bundled sub-apps. For each of these we'll create a # symlink from sub-app/Contents/Resources to the real .dylib. -- cgit v1.2.3