chromiumembedded / cef

Chromium Embedded Framework (CEF). A simple framework for embedding Chromium-based browsers in other applications.
https://bitbucket.org/chromiumembedded/cef/
Other
3.4k stars 468 forks source link

macOS: Support versioned framework directory structure #2446

Open magreenblatt opened 6 years ago

magreenblatt commented 6 years ago

Original report by me.


Starting with https://crrev.com/8685feba05 the Chromium GN config is creating app bundles that contain a versioned framework structure like the one shown below. Some newer commits like https://crrev.com/8f066ff511 are now adding dependencies on this structure (e.g. by referencing a "Versions/Current/Libraries/" directory). We should update the CEF binary distribution and CMake configuration to support this structure via creation of the necessary symlinks.

$ tree cefclient.app/Contents/Frameworks/
cefclient.app/Contents/Frameworks/
├── Chromium\ Embedded\ Framework.framework
│   ├── Chromium\ Embedded\ Framework -> Versions/Current/Chromium\ Embedded\ Framework
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
│       ├── A
│       │   ├── Chromium\ Embedded\ Framework
│       │   └── Resources
│       │       ├── Info.plist
│       │       ├── cef.pak
│       │       ├── cef_100_percent.pak
│       │       ├── cef_200_percent.pak
│       │       ├── cef_extensions.pak
│       │       ├── devtools_resources.pak
│       │       ├── en.lproj
│       │       │   └── locale.pak
│       │       ├── icudtl.dat
│       │       ├── natives_blob.bin
│       │       ├── snapshot_blob.bin
│       │       ├── v8_context_snapshot.bin
│       └── Current -> A
└── cefclient\ Helper.app
    └── Contents
        ├── Info.plist
        ├── MacOS
        │   └── cefclient\ Helper
        └── PkgInfo
magreenblatt commented 6 years ago

Add angle and swiftshader binaries to the app bundle in master revision 7f3c21b (bb). This fixes errors like the following when using software rendering:

[0725/143920.686042:ERROR:gl_implementation.cc(291)] Failed to load /Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/cefclient.app/Contents/Frameworks/Chromium Embedded Framework.framework/Versions/Current/Libraries/libswiftshader_libGLESv2.dylib: dlopen(/Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/cefclient.app/Contents/Frameworks/Chromium Embedded Framework.framework/Versions/Current/Libraries/libswiftshader_libGLESv2.dylib, 1): image not found
[0725/143920.856006:ERROR:gl_implementation.cc(291)] Failed to load /Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/cefclient.app/Contents/Frameworks/Chromium Embedded Framework.framework/Versions/Current/Libraries/libswiftshader_libGLESv2.dylib: dlopen(/Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/cefclient.app/Contents/Frameworks/Chromium Embedded Framework.framework/Versions/Current/Libraries/libswiftshader_libGLESv2.dylib, 1): no suitable image found.  Did find:
    /Users/marshall/code/chromium_git/chromium/src/out/Debug_GN_x64/cefclient.app/Contents/Frameworks/Chromium Embedded Framework.framework/Versions/Current/Libraries/libswiftshader_libGLESv2.dylib: file system sandbox blocked stat()
[0725/143920.948119:ERROR:viz_main_impl.cc(235)] Exiting GPU process due to errors during initialization
[0725/143920.957939:ERROR:gpu_process_host.cc(576)] !GpuDataManagerImpl::GpuProcessStartAllowed()
[0725/143928.947144:ERROR:software_output_device_mac.cc(59)] No previous paint buffer to copy accumulated damage from.
magreenblatt commented 6 years ago

Distribution README.txt updated in master revision 4cc3a22 (bb) and 3497 branch revision 17f0192 (bb).

magreenblatt commented 5 years ago

Issue #2738 was marked as a duplicate of this issue.

magreenblatt commented 5 years ago

Original comment by Hidetomo Katsura (Bitbucket: Hidetomo Katsura).


this issue #2446 doesn’t mention the location of dylib files.

make sure to place the dylib files directly in the “A” directory, not in the “Libraries” subdirectory. #2738:

Chromium Embedded Framework.framework/
  Chromium Embedded Framework -> Versions/Current/Chromium Embedded Framework
  Resources -> Versions/Current/Resources
  Versions/
    A/
      Chromium Embedded Framework
      Resources/
      libEGL.dylib
      libGLESv2.dylib
      libswiftshader_libEGL.dylib
      libswiftshader_libGLESv2.dylib
    Current -> A
  libEGL.dylib -> Versions/Current/libEGL.dylib
  libGLESv2.dylib -> Versions/Current/libGLESv2.dylib
  libswiftshader_libEGL.dylib -> Versions/Current/libswiftshader_libEGL.dylib
  libswiftshader_libGLESv2.dylib -> Versions/Current/libswiftshader_libGLESv2.dylib
magreenblatt commented 4 years ago

Original comment by Remo E (Bitbucket: Remo E).


I’ve tested the current CEF framework (CEF 79.1.36+g90301bd+chromium-79.0.3945.130) with Apple Store upload verification. It throws the following error:

The framework bundle (test.app/Contents/Frameworks/Chromium Embedded Framework.framework) contains 'Libraries', which should be a symbolic link -> 'Versions/Current/Libraries'. Refer to the <a href="http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html"Anatomy> of Framework Bundles for more information.

hkatsura commented 1 year ago

For those who want to preserve the symlink-based framework bundle directory structure in the build result, here is the fix.

tools/file_util.py:

@@ -119,7 +119,7 @@ def copy_dir(src, dst, quiet=True):
   """ Copy a directory tree. """
   try:
     remove_dir(dst, quiet)
-    shutil.copytree(src, dst)
+    shutil.copytree(src, dst, symlinks=True)
     if not quiet:
       sys.stdout.write('Transferring ' + src + ' directory.\n')
   except IOError as e:

tools/make_distrib.py:

@@ -1056,7 +1056,7 @@ elif platform == 'mac':
       dst_dir = os.path.join(output_dir, 'Debug')
       make_dir(dst_dir, options.quiet)
       framework_src_dir = os.path.join(
-          build_dir, '%s/Contents/Frameworks/%s.framework/Versions/A' %
+          build_dir, '%s/Contents/Frameworks/%s.framework' %
           (cefclient_app, framework_name))
       framework_dst_dir = os.path.join(dst_dir, '%s.framework' % framework_name)
       copy_dir(framework_src_dir, framework_dst_dir, options.quiet)
@@ -1084,7 +1084,7 @@ elif platform == 'mac':
       dst_dir = os.path.join(output_dir, 'Release')
       make_dir(dst_dir, options.quiet)
       framework_src_dir = os.path.join(
-          build_dir, '%s/Contents/Frameworks/%s.framework/Versions/A' %
+          build_dir, '%s/Contents/Frameworks/%s.framework' %
           (cefclient_app, framework_name))
       if mode != 'client':
         framework_dst_dir = os.path.join(dst_dir,
magreenblatt commented 1 year ago

Thanks, the above changes appear to work (also tested with the resulting CMake build).

magreenblatt commented 1 year ago

The expected framework directory structure after this change (as of M113) is:

% tree cefclient.app/Contents/Frameworks/
cefclient.app/Contents/Frameworks/
├── Chromium Embedded Framework.framework
│   ├── Chromium Embedded Framework -> Versions/Current/Chromium Embedded Framework
│   ├── Libraries -> Versions/Current/Libraries
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
│       ├── A
│       │   ├── Chromium Embedded Framework
│       │   ├── Libraries
│       │   │   ├── libEGL.dylib
│       │   │   ├── libGLESv2.dylib
│       │   │   ├── libvk_swiftshader.dylib
│       │   │   └── vk_swiftshader_icd.json
│       │   └── Resources
│       │       ├── Info.plist
│       │       ├── chrome_100_percent.pak
│       │       ├── chrome_200_percent.pak
│       │       ├── en.lproj (and other languages)
│       │       │   └── locale.pak
│       │       ├── icudtl.dat
│       │       ├── resources.pak
│       │       ├── snapshot_blob.bin
│       │       ├── v8_context_snapshot.x86_64.bin
│       └── Current -> A
└── cefclient Helper.app (and other Helper apps)
    └── Contents
        ├── Info.plist
        ├── MacOS
        │   └── cefclient Helper
        └── PkgInfo
hkatsura commented 1 year ago

my bad.

maybe it avoided symlinks on purpose because zipfile.ZipFile() doesn't support symlinks. create_zip_archive() needs to be updated to support symlinks.

tools/make_distrib.py:

def create_zip_archive(input_dir):
  """ Creates a zip archive of the specified input directory. """
  zip_file = input_dir + '.zip'
  zf = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED, True)
hkatsura commented 1 year ago

or maybe just use the zip command only on Mac.

def create_zip_archive(input_dir):
  """ Creates a zip archive of the specified input directory. """
  zip_file = input_dir + '.zip'
  if sys.platform == 'darwin':
    cmd = ['zip', '-ry', zip_file, input_dir]
    subprocess.check_call(cmd)
  else:
...
magreenblatt commented 1 year ago

We can add symlink support to create_zip_archive as shown here. create_tar_archive (for 'tar.gz' and 'tar.bz2') and create_7z_archive (for 7z when installed via brew install p7zip) already support symlinks.

magreenblatt commented 1 year ago

It may be wise to hold off on this change until we have a clear need for it. A number of tools (in my testing so far) have problems with these symlinks:

Likely other consumers of CEF will run into similar issues with their tooling.

magreenblatt commented 1 year ago

PR with the necessary changes is here: https://bitbucket.org/chromiumembedded/cef/pull-requests/605

magreenblatt commented 1 year ago

One final point: It would probably be nice to use Versions/112.2.10 instead of Versions/A as the directory structure (e.g. the actual CEF version number).

PatTheMav commented 1 year ago

The version identifier in Frameworks is not meant to represent the actual version of the dynamic library, but instead is meant to differentiate a current version from a prior version at link time:

https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html#//apple_ref/doc/uid/20002253-99920

The idea being that you keep a Framework in the filesystem, with existing consumers using the A variant (linked with in the past), but new consumers linking against the B variant pointed at by the updated Current symlink (the linker will use the actual path after resolving the symlink).

As most applications are distributed as self-contained app bundles, there are no other consumers that might require a prior version of the library in the same framework - the framework contains a single version variant, so Versions/A is the correct naming.

hkatsura commented 1 year ago

i just hit an issue with the cef client cmake build while the cef directory is structured "correctly" with symlinks.

cmake copy_directory follows symlinks (and there is no option to disable following). so, the "correct" cef framework directory with symlinks gets destroyed (malformed) during copy_directory unless you somehow workaround the cmake copy_directory. sigh...

PatTheMav commented 1 year ago

CMake can do deep copies if you use the MACOSX_PACKAGE_LOCATION source file property. Alternatively use the XCODE_EMBED_FRAMEWORKS target property to have Xcode automatically copy and codesign Frameworks for use in an application bundle.

hkatsura commented 1 year ago

EDIT: my bad. it was already part of the pull request. obviously, i wasn't paying attention. https://bitbucket.org/chromiumembedded/cef/pull-requests/605

thanks for tips! replacing "cmake copy_directory" with "cp" seems to work.

-    COMMAND ${CMAKE_COMMAND} -E copy_directory
+    COMMAND cp -a