base_paths_mac.mm 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // Defines base::PathProviderMac which replaces base::PathProviderPosix for Mac
  5. // in base/path_service.cc.
  6. #include <dlfcn.h>
  7. #import <Foundation/Foundation.h>
  8. #include <mach-o/dyld.h>
  9. #include <stdint.h>
  10. #include "base/base_paths.h"
  11. #include "base/check_op.h"
  12. #include "base/files/file_path.h"
  13. #include "base/files/file_util.h"
  14. #include "base/mac/bundle_locations.h"
  15. #include "base/mac/foundation_util.h"
  16. #include "base/notreached.h"
  17. #include "base/path_service.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/threading/thread_restrictions.h"
  20. #include "build/build_config.h"
  21. namespace {
  22. void GetNSExecutablePath(base::FilePath* path) {
  23. DCHECK(path);
  24. // Executable path can have relative references ("..") depending on
  25. // how the app was launched.
  26. uint32_t executable_length = 0;
  27. _NSGetExecutablePath(NULL, &executable_length);
  28. DCHECK_GT(executable_length, 1u);
  29. std::string executable_path;
  30. int rv = _NSGetExecutablePath(
  31. base::WriteInto(&executable_path, executable_length),
  32. &executable_length);
  33. DCHECK_EQ(rv, 0);
  34. // _NSGetExecutablePath may return paths containing ./ or ../ which makes
  35. // FilePath::DirName() work incorrectly, convert it to absolute path so that
  36. // paths such as DIR_SRC_TEST_DATA_ROOT can work, since we expect absolute
  37. // paths to be returned here.
  38. // TODO(bauerb): http://crbug.com/259796, http://crbug.com/373477
  39. base::ThreadRestrictions::ScopedAllowIO allow_io;
  40. *path = base::MakeAbsoluteFilePath(base::FilePath(executable_path));
  41. }
  42. // Returns true if the module for |address| is found. |path| will contain
  43. // the path to the module. Note that |path| may not be absolute.
  44. [[nodiscard]] bool GetModulePathForAddress(base::FilePath* path,
  45. const void* address);
  46. bool GetModulePathForAddress(base::FilePath* path, const void* address) {
  47. Dl_info info;
  48. if (dladdr(address, &info) == 0)
  49. return false;
  50. *path = base::FilePath(info.dli_fname);
  51. return true;
  52. }
  53. } // namespace
  54. namespace base {
  55. bool PathProviderMac(int key, base::FilePath* result) {
  56. switch (key) {
  57. case base::FILE_EXE:
  58. GetNSExecutablePath(result);
  59. return true;
  60. case base::FILE_MODULE:
  61. return GetModulePathForAddress(result,
  62. reinterpret_cast<const void*>(&base::PathProviderMac));
  63. case base::DIR_APP_DATA: {
  64. bool success = base::mac::GetUserDirectory(NSApplicationSupportDirectory,
  65. result);
  66. #if BUILDFLAG(IS_IOS)
  67. // On IOS, this directory does not exist unless it is created explicitly.
  68. if (success && !base::PathExists(*result))
  69. success = base::CreateDirectory(*result);
  70. #endif // BUILDFLAG(IS_IOS)
  71. return success;
  72. }
  73. case base::DIR_SRC_TEST_DATA_ROOT:
  74. #if BUILDFLAG(IS_IOS)
  75. // On iOS, there is no access to source root, however, the necessary
  76. // resources are packaged into the test as assets.
  77. return PathService::Get(base::DIR_ASSETS, result);
  78. #else
  79. // Go through PathService to catch overrides.
  80. if (!PathService::Get(base::FILE_EXE, result))
  81. return false;
  82. // Start with the executable's directory.
  83. *result = result->DirName();
  84. if (base::mac::AmIBundled()) {
  85. // The bundled app executables (Chromium, TestShell, etc) live five
  86. // levels down, eg:
  87. // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium
  88. *result = result->DirName().DirName().DirName().DirName().DirName();
  89. } else {
  90. // Unit tests execute two levels deep from the source root, eg:
  91. // src/xcodebuild/{Debug|Release}/base_unittests
  92. *result = result->DirName().DirName();
  93. }
  94. return true;
  95. #endif // BUILDFLAG(IS_IOS)
  96. case base::DIR_USER_DESKTOP:
  97. #if BUILDFLAG(IS_IOS)
  98. // iOS does not have desktop directories.
  99. NOTIMPLEMENTED();
  100. return false;
  101. #else
  102. return base::mac::GetUserDirectory(NSDesktopDirectory, result);
  103. #endif
  104. case base::DIR_ASSETS:
  105. #if BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_MACCATALYST)
  106. // On iOS, the assets are located next to the module binary.
  107. return PathService::Get(base::DIR_MODULE, result);
  108. #else
  109. if (!base::mac::AmIBundled()) {
  110. return PathService::Get(base::DIR_MODULE, result);
  111. }
  112. #if BUILDFLAG(IS_IOS_MACCATALYST)
  113. *result = base::mac::MainBundlePath()
  114. .Append(FILE_PATH_LITERAL("Contents"))
  115. .Append(FILE_PATH_LITERAL("Resources"));
  116. #else
  117. *result = base::mac::FrameworkBundlePath().Append(
  118. FILE_PATH_LITERAL("Resources"));
  119. #endif // BUILDFLAG(IS_IOS_MACCATALYST)
  120. return true;
  121. #endif // BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_MACCATALYST)
  122. case base::DIR_CACHE:
  123. return base::mac::GetUserDirectory(NSCachesDirectory, result);
  124. default:
  125. return false;
  126. }
  127. }
  128. } // namespace base