当前位置:网站首页>Research on the management of three party library by cocoapods

Research on the management of three party library by cocoapods

2020-12-08 14:39:49 osc_ 0sqf9hqy

Python The actual combat community

Java The actual combat community

Long press to identify the QR code below , Add as needed

Scan code, pay attention to add customer service

Into the Python community ▲

Scan code, pay attention to add customer service

Into the Java community

The author 丨 zhangferry

Source iOS The road to growth


CocoaPods yes iOS Third party library management tools are often used in development , It's necessary for us to understand in depth what impact it has on the project , And how it manages these libraries .

Use pod Install tripartite Library

We create a new module called FFDemo Of Swift project , Its directory structure is like this

├── FFDemo
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   ├── Base.lproj
│   ├── Info.plist
│   ├── SceneDelegate.swift
│   └── ViewController.swift
└── FFDemo.xcodeproj
    ├── project.pbxproj
    ├── project.xcworkspace
    └── xcuserdata

Then we carry out pod init Create a Podfile Templates , Introduce these two tripartite libraries into it :

target 'FFDemo' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for FFDemo
  pod 'MJRefresh''~> 3.5.0'
  pod 'Moya'

end

Successful execution pod install We then introduced these two libraries into the project , Now the project directory is like this :

├── FFDemo
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   ├── Base.lproj
│   ├── Info.plist
│   ├── SceneDelegate.swift
│   └── ViewController.swift
├── FFDemo.xcodeproj
│   ├── project.pbxproj
│   ├── project.xcworkspace
│   └── xcuserdata
├── FFDemo.xcworkspace
│   └── contents.xcworkspacedata
├── Podfile
├── Podfile.lock
└── Pods
    ├── Alamofire
    ├── Headers
    ├── LocalPodspecs
    ├── MJRefresh
    ├── Manifest.lock
    ├── Moya
    ├── Pods.xcodeproj
    └── TargetSupportFiles

From the catalogue , except pod init Introduced Podfile, The rest three parts :FFDemo.xcworkspace、Podfile.lock、Pods The catalogue is made up of pod install Later generated . Let's focus on these three parts .

CocoaPods What to install

xcworkspace file

The file contains a file called contents.xcworkspacedata The file of , Its content is like this :

<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:FFDemo.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>

Use xml The format includes the dependency within the tag .

xcworkspace It's a project container , When there is more than one project You can use when you need to depend on each other xcworkspace Organize them .pod When you first install the tripartite library, a file called Pods.xcodeproj Of project Managing the tripartite Library , Then the project And the main project project adopt workspace Association . In this way, we can introduce the tripartite library into the main project , And the tripartite library is made up of Pods.xcodeproj Unified management , It won't interfere with our original project .

Podfile.lock

Podfile.lock The contents of the document are as follows :

PODS:
  - Alamofire (5.3.0)
  - MJRefresh (3.5.0)
  - Moya (14.0.0):
    - Moya/Core (= 14.0.0)
  - Moya/Core (14.0.0):
    - Alamofire (~> 5.0)

DEPENDENCIES:
  - MJRefresh (~> 3.5.0)
  - Moya

SPEC REPOS:
  trunk:
    - Alamofire
    - MJRefresh
    - Moya

SPEC CHECKSUMS:
  Alamofire: 2c792affbdc2f18016e08fdbcacd60aebe1ba593
  MJRefresh: 6afc955813966afb08305477dd7a0d9ad5e79a16
  Moya: 5b45dacb75adb009f97fde91c204c1e565d31916

PODFILE CHECKSUM: 073f3d6d9f03e6a76838ca3719df48ae6cc01450

COCOAPODS: 1.9.3

because Podfile No version number can be specified in the file , And the version information is very important , So there was Podfile.lock, It records complete version information and dependencies . Its content includes the following big blocks

PODS

PODS Refers to the specific version number of the current reference library , You can see that we didn't introduce Alamofire, But in PODS It does have it in it . This is because Moya I rely on it ,Moya There is a definition of subspec It's called Core, This is a Moya/Core The origin of the writing method .pod It's through libraries podspec File to find the corresponding dependency , Here we can take a simple look at Moya Part of podspeec The contents of the document Moya.podspec:

Pod::Spec.new do |s|
  s.default_subspecs = "Core"

  s.subspec "Core" do |ss|
    ss.source_files  = "Sources/Moya/""Sources/Moya/Plugins/"
    ss.dependency "Alamofire""~> 5.0"
    ss.framework  = "Foundation"
  end
end

DEPENDENCIES

DEPENDENCIES by pod Description of the library , The content here is the same as Podfile Li's way of writing . Because we designated MJRefresh Version number of , There is no designation Moya Version number of , So the content here is the same .

SPEC REPOS

Here is the warehouse information , Which tripartite libraries are installed , Which warehouse are they from .

trunk It's the name of the shared warehouse , Its address is https://github.com/CocoaPods/Specs.git, Most of the third-party libraries for external use come from here . Usually, we also rely on private libraries within the company , Private library information will also be displayed here .

SPEC CHECKSUM

Here we describe the checksums of the three-party libraries , The check sum algorithm is based on the current installed version of the tripartite library podspec Document requirements SHA1. such as MJRefresh Checksum :6afc955813966afb08305477dd7a0d9ad5e79a16. We installed MJRefresh The version is 3.5.0, It's local podspec The file path is :~/.cocoapods/repos/trunk/Specs/0/f/b/MJRefresh/3.5.0/MJRefresh.podspec.json.

This path can be added when installing the library --verbose The parameters are checked in the output log . We passed the content of the document through openssl seek sha1 Abstract :

$ pod ipc spec ~/.cocoapods/repos/trunk/Specs/0/f/b/MJRefresh/3.5.0/MJRefresh.podspec.json | openssl sha1
$ 6afc955813966afb08305477dd7a0d9ad5e79a16

Because it's right podspec.json The content requires sha1, So as long as the content changes a little , The resulting checksums will be very different , And that's what checksums are designed for :podspec A change in the file means that the version information has changed , You need to resynchronize the code .

You may have noticed , We usually make private pod, The file that controls configuration information is podspec Format , Why do local files become json Format ?

This is because json Higher format compatibility and easier batch processing , official Spec All library configuration files of the warehouse are converted to json Format . When we make a private library, we can use podspec Push the format of the remote warehouse , But when parsing the file later pod Internal search will still turn it into json Format . The above command contains podsepc turn json Ordered by , turn json The order is as follows :

$ pod ipc spec ModuleName.podspec

PODFILE CHECKSUM

This check sum is for Podfile Check sum of content , If Podfile The content has changed , The value will change as well . The calculation method is as follows :

$ openssl sha1 filePath/Podfile

COCOAPODS: 1.9.3

This represents the current use of CocoaPod Version number , Remote version management should ensure that you use pod The version number is the same .

Pods

Manifest.lock

Manifest.lock yes Podfile.lock Copy of , It's in Pods Inside the directory . It works like this , We usually don't put Pods Files are put into version management , But the Podfile.lock Put it in version management . In this case, whether the code needs to be updated after pulling pod, By comparing the local Manifest.lock And remote Podfile.lock Whether it is the same or not .

Targets Support Files

Pods Installation dependencies are organized in this way

One Pods Of Project Here are three Targets, Three of them are installed dependency Libraries , the last one Pods-FFDemo It's linked to three libraries Framework, That is to say Pods This Project Of Targets.

Pods-Demo Framework

Look at this first Demo Of Framework, It will be used for reference dependencies in engineering projects

This library doesn't get thrown into the bag , because Do Not Embed Representation is not an inclusive relationship .

The configuration files under this project have these :

License agreement file Two with acknowledgements Named files are used to manage pod Library license agreement , That is, the tripartite library must have LICENSE file , That's why we're making pod We will be asked to specify the software protocol .

Framework file It also includes the use for management Module Of modulemap and umbrella.h file .modulemap It's right Module The statement of , Make Framework We always need that document , Its contents are as follows :

framework module Pods_FFDemo {
  umbrella header "Pods-FFDemo-umbrella.h"

  export *
  module * { export * }
}

It points to a umbrella The header file , This is making Framework Required header file ,modulemap and umbrella.h Will be creating Module Automatically generate , It is not recommended to modify their relationships manually .

dummy.m file

This is actually an empty .m file

#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_FFDemo : NSObject
@end
@implementation PodsDummy_Pods_FFDemo
@end

So why have this thing , The package that includes all the three-party libraries will also contain a dummy file . I am here stackoverflow[1] Found an explanation :Xcode The compilation of is dependent on .m Of documents , If a library doesn't have .m file , Will not be compiled , In order to prevent this, an empty... Is added to each library .m file .

xcconfig file

xcconfig File is Build Setting Configuration item file form , It takes precedence over Xcode Internal Build Setting. Take a look at a pod Generated debug Mode of xcconfig file .

ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Moya"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "Foundation" -framework "MJRefresh" -framework "Moya"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

xcconfig Another function is to set parameters , For example, we are familiar with PODS_ROOT=${SRCROOT}/PODS, It represents... In the root of the project PODS File directory . The other two items are used to help us find the third party Library in the project FRAMEWORK_SEARCH_PATHS and HEADER_SEARCH_PATHS It is also defined within the document , These configurations will reflect Build Settings Inside :

Three party Library Framework

Each of the three libraries also has some configuration files , Their file formats are basically the same , The file function is similar to that described above , The picture below is Moya Configuration file for ,Xcode in Pods > Pods > Moya > Support Files The corresponding file is the content .

image-20201114150517801

We can think of a question , When an installed third-party library needs to rely on other libraries, how does it find this library ?Moya It is necessary to use Alamofire Of API Of , There will be import Alamofire The operation of . With the above , It can be learned that Framework The reference to is needed in Build Setting I should have Target, What references are there . So this is also Framework in xcconfig Role of documents , Can be in Moya Of xcconfig I found this in the file :

FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire"

And it refers to the same as the main project Alamofire The path of .

Build Phases

This is the place to set the compile phase configuration , When the first time pod install After success , There will be more [CP] The configuration item at the beginning (CP namely CocoaPods abbreviation ), They are all made by CocoPods Added script content , Execution order from top to bottom .

New System Build

Before we talk about compiling scripts, let's just say New Build System.

New Build System yes Xcode10 Then Apple launched a new build system , The new build system optimizes the compilation process [2] A lot of work , Although to Xcode12 Still compatible with older versions of Legacy Build System, But it has been marked for removal , Our projects and libraries should be built with a new build system . And the new build system comes with an additional list of inputs and outputs when running scripts .

This is to control whether the corresponding script needs to be executed for each compilation ,input and output A file can be in the form of a single file , If you can put too many files in xcfilelist In the file list of .

If not provided input and output, The script runs every time the build is done . If provided , It will never run before 、 Run again if an input file is changed or an output file is missing .

Note that these are the default logic for build scripts ,Xcode It also provides Run Scripts Custom behavior of , Default tick :Based on dependency analysis, It represents the above logic . If you provide input and output, you need to run every time , Turn off this option .

[CP] Check Pods Manifest.lock

The script is at the top , without Dependencies, The script is executed when you start compiling , Its contents are as follows :

diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
if [ $? != 0 ] ; then
    # print error to STDERR
    echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
    exit 1
fi
# This output is used by Xcode 'outputs' to avoid re-running this script phase.
echo "SUCCESS" > "${SCRIPT_OUTPUT_FILE_0}"

The effect is to compare Podfile.lock and Manifest.lock Whether the documents are the same , If it is different, it will output error message :error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation., And execute exit , This will lead to errors in subsequent projects , Compilation cannot continue .

This error is more common , Appears in pull remote code , Distal pod Depending on local inconsistencies . At this point, we can follow the instructions , perform pod install command , according to Podfile And far away Podfile.lock Generate a new Manifest.lock file .

[CP] Copy Pods Resources

This script will be added if the resource is included in the tripartite library introduced by static library , Its function is to copy the resource files of the tripartite library to the project .

It's done by running the following script :

"${PODS_ROOT}/Target Support Files/Pods-FFDemo/Pods-FFDemo-resources.sh"

Pods-FFDemo-resources.sh The file in Pods In the table of contents , There is a key function in the script install_resource

install_resource()
{
  if [[ "$1" = /* ]] ; then
    RESOURCE_PATH="$1"
  else
    RESOURCE_PATH="${PODS_ROOT}/$1"
  fi
  if [[ ! -e "$RESOURCE_PATH" ]] ; then
    cat << EOM
error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
EOM
    exit 1
  fi
  case $RESOURCE_PATH in
    *.storyboard)
      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
      ;;
    *.xib)
      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
      ;;
    *.framework)
      echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
      mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}$RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
      ;;
    *.xcassets)
      ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
      XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
      ;;
    *)
      echo "$RESOURCE_PATH" || true
      echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
      ;;
  esac
}

Deleted a part of the log content , Inside it is mainly a switch sentence , According to the type of resource file, different synchronization operations are performed . Here we focus on the following several important format file processing methods .

storyboard and xib Format

These two resource files need to be compiled and processed , utilize ibtool The order was changed to sotryboardc and nib Format .

xcassets Format

The images here will eventually be packaged into Assets.car For program use , Need to use actool.

Bundle、plist、png And so on

The resources of other classes will go to switch At the end of the sentence , Assign the resource path to $RESOURCES_TO_COPY, In the later code, through rsync command , Synchronize resources to the directory of the build package .

The script will print a lot of logs , In the use of CocoaPods If you encounter resource related problems, you can follow the error log to speculate and locate the cause of the error .

[CP] Embed Pods Frameworks

Where the script is run directly Pods-FFDemo-frameworks.sh.

"${PODS_ROOT}/Target Support Files/Pods-FFDemo/Pods-FFDemo-frameworks.sh"

Maybe you remember the above pod The dependency of multiple libraries will be made into a combined library , But the library is introduced into the main project in the form of dependency , But these libraries are needed to run the program , When we package, we need to put the Libraries Embed Into the project , And it's the script that does the job .

# Copies and strips a vendored framework
install_framework()
{
  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"

  # other code...

  # Strip invalid architectures so "fat" simulator / device frameworks work on device
  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
    strip_invalid_archs "$binary"
  fi

  # Resign the code if required by the build settings to avoid unstable apps
  code_sign_if_enabled "${destination}/$(basename "$1")"
}

The main content of the script is to call install_framework function , take framework The content is synchronized into the build package . There are also several key methods in this function ,strip_invalid_archs Used to remove useless Architecture ,code_sign_if_enabled be used for framwork Signature .

Reference material

[1]

Why do cocoapod create a dummy class for every pod?: https://stackoverflow.com/questions/39160655/why-do-cocoapod-create-a-dummy-class-for-every-pod

[2]

Speeding up your custom Xcode build scripts: https://nathanwong.co.uk/post/xcode-buildphases/

 Programmer column   Scan code and pay attention to customer service   Press and hold to recognize the QR code below to enter the group 

Recent highlights are recommended :  

  The employee was fired for taking too long to go to the toilet

  Programmer continuous 15 I worked overtime until early morning 2 Tearful in restaurant !

  still try...catch? If it is, then you out 了 !

 Python Very slowly ?Python My father's words are clear


Here's a look Good articles to share with more people ↓↓


版权声明
本文为[osc_ 0sqf9hqy]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201208143916007e.html