linkMacosPodspec function

void linkMacosPodspec(
  1. String pluginName,
  2. List<String> moduleLibs, {
  3. String baseDir = '.',
  4. List<ModuleInfo>? moduleInfos,
})

Implementation

void linkMacosPodspec(String pluginName, List<String> moduleLibs, {String baseDir = '.', List<ModuleInfo>? moduleInfos}) {
  final nitroNativePath = resolveNitroNativePath(baseDir);
  final podspecFile = File(p.join(baseDir, 'macos', '$pluginName.podspec'));
  if (!podspecFile.existsSync()) return;
  var content = podspecFile.readAsStringSync();
  bool modified = false;
  if (!content.contains("s.swift_version = '5.9'")) {
    content = content.replaceFirst(RegExp(r"s\.swift_version\s*=\s*'.+?'"), "s.swift_version = '5.9'");
    modified = true;
  }
  if (!content.contains("s.platform = :osx, '10.15'")) {
    if (RegExp(r"s\.platform\s*=\s*:osx").hasMatch(content)) {
      content = content.replaceFirst(RegExp(r"s\.platform\s*=\s*:osx,\s*'.+?'"), "s.platform = :osx, '10.15'");
    } else {
      // Insert platform line after the spec name line
      content = content.replaceFirst(RegExp(r"(s\.name\s*=.+\n)"), r"$1  s.platform = :osx, '10.15'\n");
    }
    modified = true;
  }
  if (!content.contains('HEADER_SEARCH_PATHS')) {
    content = content.replaceFirst(
      's.pod_target_xcconfig = {',
      "s.pod_target_xcconfig = {\n    'HEADER_SEARCH_PATHS' => '\$(inherited) \"\${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/nitro/src/native\" \"\${PODS_TARGET_SRCROOT}/../src\" \"\${PODS_TARGET_SRCROOT}/../lib/src/generated/cpp\"',",
    );
    modified = true;
  } else {
    if (!content.contains('PODS_TARGET_SRCROOT}/../src') || !content.contains('lib/src/generated/cpp')) {
      final match = RegExp(r"'HEADER_SEARCH_PATHS'\s*=>\s*'([^']+)'").firstMatch(content);
      if (match != null) {
        var paths = match.group(1)!;
        if (!paths.contains('PODS_TARGET_SRCROOT}/../src')) {
          paths += ' "\${PODS_TARGET_SRCROOT}/../src"';
        }
        if (!paths.contains('lib/src/generated/cpp')) {
          paths += ' "\${PODS_TARGET_SRCROOT}/../lib/src/generated/cpp"';
        }
        content = content.replaceFirst(match.group(0)!, "'HEADER_SEARCH_PATHS' => '$paths'");
        modified = true;
      }
    }
  }
  if (!content.contains("'DEFINES_MODULE' => 'YES'")) {
    content = content.replaceFirst('s.pod_target_xcconfig = {', "s.pod_target_xcconfig = {\n    'DEFINES_MODULE' => 'YES',");
    modified = true;
  }
  if (!content.contains("'CLANG_CXX_LANGUAGE_STANDARD'") && !content.contains('c++17')) {
    content = content.replaceFirst('s.pod_target_xcconfig = {', "s.pod_target_xcconfig = {\n    'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',");
    modified = true;
  }
  if (!content.contains("s.dependency 'nitro'")) {
    content = content.replaceFirst('s.pod_target_xcconfig = {', "s.dependency 'nitro'\n  s.pod_target_xcconfig = {");
    modified = true;
  }
  // Ensure generated Swift bridges are included from their canonical location.
  if (!content.contains("lib/src/generated/swift")) {
    final before = content;
    content = content.replaceFirstMapped(
      RegExp(r"(s\.source_files\s*=\s*'[^']+')"),
      (m) => "${m.group(1)}, '../lib/src/generated/swift/**/*.swift'",
    );
    if (content == before) {
      content = content.replaceFirst(
        's.pod_target_xcconfig = {',
        "s.source_files = 'Classes/**/*', '../lib/src/generated/swift/**/*.swift'\n  s.pod_target_xcconfig = {",
      );
    }
    modified = true;
  }
  if (modified) podspecFile.writeAsStringSync(content);
  createSharedHeaders(nitroNativePath, baseDir: baseDir);
  final classesDir = Directory(p.join(baseDir, 'macos', 'Classes'))..createSync(recursive: true);
  File(p.join(classesDir.path, 'dart_api_dl.c')).writeAsStringSync('#include "../../src/dart_api_dl.c"\n');
  // Remove stale .bridge.g.swift copies — now compiled from canonical location.
  _cleanStaleSwiftBridges(classesDir.path);
  syncBridgeFiles(baseDir, platform: 'macos');

  // Link the main project source files.
  final cppInSrc = File(p.join(baseDir, 'src', '$pluginName.cpp'));
  if (cppInSrc.existsSync()) {
    cleanRedundantIncludes(cppInSrc);
    File(p.join(classesDir.path, '$pluginName.cpp')).writeAsStringSync('// Generated by nitrogen link — do not edit.\n#include "../../src/$pluginName.cpp"\n');
  }
  final cInSrc = File(p.join(baseDir, 'src', '$pluginName.c'));
  if (cInSrc.existsSync()) {
    cleanRedundantIncludes(cInSrc);
    File(p.join(classesDir.path, '$pluginName.c')).writeAsStringSync('#include "../../src/$pluginName.c"\n');
  }

  // Link C++ module implementation files for macOS — same logic as iOS above.
  // Only AppleNativeImpl.cpp modules get a Hybrid*.cpp forwarder in macos/Classes/.
  if (moduleInfos != null) {
    final libDir = Directory(p.join(baseDir, 'lib'));
    final specFiles = libDir.existsSync() ? libDir.listSync(recursive: true).whereType<File>().where((f) => f.path.endsWith('.native.dart')).toList() : <File>[];
    final appleCppLibs = specFiles.where(isAppleCppModule).map((f) {
      final stem = p.basename(f.path).replaceAll(RegExp(r'\.native\.dart$'), '');
      return extractLibNameFromSpec(f) ?? stem;
    }).toSet();

    for (final m in moduleInfos.where((m) => m.isCpp)) {
      final className = _toPascalCase(m.lib);
      final forwarderFile = File(p.join(classesDir.path, 'Hybrid$className.cpp'));
      if (appleCppLibs.contains(m.lib)) {
        final implSrc = File(p.join(baseDir, 'src', 'Hybrid$className.cpp'));
        if (implSrc.existsSync()) {
          forwarderFile.writeAsStringSync('// Generated by nitrogen link — do not edit.\n#include "../../src/Hybrid$className.cpp"\n');
        }
      } else {
        if (forwarderFile.existsSync()) forwarderFile.deleteSync();
      }
    }
  }
}