Commit 5dab3631 authored by Fabio Pelosin's avatar Fabio Pelosin

Merge branch 'lookback-pods-by-config'

* lookback-pods-by-config: (45 commits)
  [UserProjectIntegrator] Warn about xcconfig override after every installation /2
  [UserProjectIntegrator] Warn about xcconfig override after every installation
  [AggregateTarget] Add #user_targets
  [AggregateTarget] Documentation cleanup
  [Bundle] Update
  [PodTarget] Ensure that subspecs are on the same configurations
  add a space to the override warning
  [Docs] Some small fixes.
  [Changelog]
  [Integration] Disable xcproj in Xcodeproj
  [Project] Coherce preprocessor definitions to an array
  [PrivatePodXcconfig] Add inherited to GCC_PREPROCESSOR_DEFINITIONS
  [Project] Add preprocessor definition for build configurations
  [TargetEnvironmentHeader] Refactor
  [AggretateTarget] Expose specs by build configuration
  [PrefixHeader] Style fixes
  [Xcodeproj]: Fix issue for libraries with a space in the name
  [Examples] Update integration
  [Installer] Use downcase to validate configurations
  [XCConfigIntegrator] Downcase the name of the build configurations
  ...

Conflicts:
	CHANGELOG.md
	Gemfile.lock
	lib/cocoapods/installer.rb
	lib/cocoapods/target/aggregate_target.rb
	spec/integration.rb
parents 5870462b bbcf3cee
......@@ -6,6 +6,18 @@ To install or update CocoaPods see this [guide](http://docs.cocoapods.org/guides
##### Enhancements
* Add support to specify dependencies per build configuration.
pod 'PonyDebugger', :configurations => ['Debug']
Currently configurations can only be specified per single Pod.
[Joachim Bengtsson](https://github.com/nevyn)
[Eloy Durán](https://github.com/alloy)
[Fabio Pelosin][irrationalfab]
[#1791](https://github.com/CocoaPods/CocoaPods/pull/1791)
[#1668](https://github.com/CocoaPods/CocoaPods/pull/1668)
[#731](https://github.com/CocoaPods/CocoaPods/pull/731)
* Added hooks for plugins. Currently only the installer hook is supported.
A plugin can register itself to be activated after the installation with the
following syntax:
......@@ -27,6 +39,14 @@ To install or update CocoaPods see this [guide](http://docs.cocoapods.org/guides
[Marius Rackwitz][mrackwitz]
[#2371](https://github.com/CocoaPods/CocoaPods/issues/2371)
* Include configurations a user explicitly specifies in their Podfile when the
`--no-integrate` option is specified.
[Eloy Durán](https://github.com/alloy)
* Properly quote the `-isystem` values in the xcconfig files.
[Eloy Durán](https://github.com/alloy)
##### Bug Fixes
* Fixed pod repo push to first check if Specs directory exists and if so push
......
GIT
remote: https://github.com/CocoaPods/CLAide.git
revision: e64c76edd9dbc540776772097b78ae27720eccbb
revision: 303b96021e0fdd0003a71f1f9bf6823a6262ba12
branch: master
specs:
claide (0.6.1)
GIT
remote: https://github.com/CocoaPods/Core.git
revision: 75028fcea7c8e02cff17ada95bb17f123ac949cc
revision: 2e66303717d72b23a98ea5341dc5b627f3eb97e7
branch: master
specs:
cocoapods-core (0.33.1)
......@@ -18,7 +18,7 @@ GIT
GIT
remote: https://github.com/CocoaPods/Xcodeproj.git
revision: f835e6e1145812ac04ac4b658321c727c2967547
revision: 13733a36b29d2a9698c3e2563b52a0b6339e90df
branch: master
specs:
xcodeproj (0.18.0)
......@@ -28,7 +28,7 @@ GIT
GIT
remote: https://github.com/CocoaPods/cocoapods-downloader.git
revision: 27a5c58d7d7aaf8886c47624260d167fa776175c
revision: 0e02c9da094a82c5049cbf7266d9eefef2fd5552
branch: master
specs:
cocoapods-downloader (0.6.1)
......@@ -43,7 +43,7 @@ GIT
GIT
remote: https://github.com/CocoaPods/cocoapods-trunk.git
revision: c62f86f83bf77faf8baf80359cc511dd1014fcfb
revision: 72e02a7ad3f429a6e76e602a238e91eb55c44165
branch: master
specs:
cocoapods-trunk (0.1.4)
......
......@@ -19,9 +19,10 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1489BC7C65DCCDA21BD9C10D /* Pods-AFNetworking Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.release.xcconfig"; path = "Pods/Pods-AFNetworking Example.release.xcconfig"; sourceTree = "<group>"; };
206C0CA2FCD1CCE9783CC39F /* Pods-AFNetworking Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.debug.xcconfig"; path = "Pods/Pods-AFNetworking Example.debug.xcconfig"; sourceTree = "<group>"; };
6935B1E417A24F0E958977ED /* libPods-AFNetworking Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
B304CCE7177D58DD00F4FC85 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; };
E74E8E7AFD3F4DCF9FEAB5B4 /* Pods-AFNetworking Example.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.xcconfig"; path = "Pods/Pods-AFNetworking Example.xcconfig"; sourceTree = "<group>"; };
F8129BFB1591061B009BFE23 /* AFNetworking Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AFNetworking Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
F8129BFF1591061B009BFE23 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F8129C021591061B009BFE23 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
......@@ -60,7 +61,8 @@
F8129C051591061B009BFE23 /* Classes */,
F8129BFE1591061B009BFE23 /* Frameworks */,
F8129BFC1591061B009BFE23 /* Products */,
E74E8E7AFD3F4DCF9FEAB5B4 /* Pods-AFNetworking Example.xcconfig */,
206C0CA2FCD1CCE9783CC39F /* Pods-AFNetworking Example.debug.xcconfig */,
1489BC7C65DCCDA21BD9C10D /* Pods-AFNetworking Example.release.xcconfig */,
);
indentWidth = 4;
sourceTree = "<group>";
......@@ -296,7 +298,7 @@
};
F8129C1A1591061B009BFE23 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E74E8E7AFD3F4DCF9FEAB5B4 /* Pods-AFNetworking Example.xcconfig */;
baseConfigurationReference = 206C0CA2FCD1CCE9783CC39F /* Pods-AFNetworking Example.debug.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
......@@ -310,7 +312,7 @@
};
F8129C1B1591061B009BFE23 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E74E8E7AFD3F4DCF9FEAB5B4 /* Pods-AFNetworking Example.xcconfig */;
baseConfigurationReference = 1489BC7C65DCCDA21BD9C10D /* Pods-AFNetworking Example.release.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
......
......@@ -33,10 +33,11 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
08ACA902EBEF3347432C3442 /* Pods-AFNetworking iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.release.xcconfig"; path = "Pods/Pods-AFNetworking iOS Example.release.xcconfig"; sourceTree = "<group>"; };
17E95B75D4453CE0BA3028FF /* Pods-AFNetworking iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.debug.xcconfig"; path = "Pods/Pods-AFNetworking iOS Example.debug.xcconfig"; sourceTree = "<group>"; };
2982AD3117107C0000FFF048 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; };
50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
9D87F8FEDE4A4313B0D579A3 /* libPods-AFNetworking iOS Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking iOS Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
AE3A9F4CC86440AAA3CF5792 /* Pods-AFNetworking iOS Example.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.xcconfig"; path = "Pods/Pods-AFNetworking iOS Example.xcconfig"; sourceTree = "<group>"; };
F8129C3815910830009BFE23 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = SOURCE_ROOT; };
F8129C7215910C37009BFE23 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
F8129C7315910C37009BFE23 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
......@@ -130,7 +131,8 @@
F8E469ED1395812A00DB05C8 /* Images */,
F8E469631395739D00DB05C8 /* Frameworks */,
F8E469611395739C00DB05C8 /* Products */,
AE3A9F4CC86440AAA3CF5792 /* Pods-AFNetworking iOS Example.xcconfig */,
17E95B75D4453CE0BA3028FF /* Pods-AFNetworking iOS Example.debug.xcconfig */,
08ACA902EBEF3347432C3442 /* Pods-AFNetworking iOS Example.release.xcconfig */,
);
indentWidth = 4;
sourceTree = "<group>";
......@@ -356,7 +358,7 @@
};
F8E469821395739D00DB05C8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AE3A9F4CC86440AAA3CF5792 /* Pods-AFNetworking iOS Example.xcconfig */;
baseConfigurationReference = 17E95B75D4453CE0BA3028FF /* Pods-AFNetworking iOS Example.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
......@@ -378,7 +380,7 @@
};
F8E469831395739D00DB05C8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AE3A9F4CC86440AAA3CF5792 /* Pods-AFNetworking iOS Example.xcconfig */;
baseConfigurationReference = 08ACA902EBEF3347432C3442 /* Pods-AFNetworking iOS Example.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
......
......@@ -9,18 +9,18 @@ module Pod
#
class PrefixHeader
# @return [Platform] the platform for which the prefix header will be
# generated.
# @return [Array<FileAccessor>] The file accessors for which to generate
# the prefix header.
#
attr_reader :file_accessors
attr_reader :platform
# @return [Array<LocalPod>] the LocalPod for the target for which the
# prefix header needs to be generated.
# @return [Platform] the platform for which the prefix header will be
# generated.
#
# attr_reader :consumers
attr_reader :platform
# @return [Array<String>] any header to import (with quotes).
# @return [Array<String>] The list of the headers to import (with
# quotes).
#
attr_reader :imports
......@@ -40,12 +40,14 @@ module Pod
# added to the top of the prefix header. For OS X `Cocoa/Cocoa.h`
# is imported.
#
# @note Only unique prefix_header_contents are added to the prefix header.
# @note Only unique prefix_header_contents are added to the prefix
# header.
#
# @return [String]
#
# @todo Subspecs can specify prefix header information too.
# @todo Check to see if we have a similar duplication issue with file_accessor.prefix_header.
# @todo Check to see if we have a similar duplication issue with
# file_accessor.prefix_header.
#
def generate
result = "#ifdef __OBJC__\n"
......
require 'active_support/core_ext/string/strip'
module Pod
module Generator
......@@ -23,14 +25,15 @@ module Pod
#
class TargetEnvironmentHeader
# @return [Array<LocalPod>] the specifications installed for the target.
# @return [Hash{String => LocalPod}] the specifications installed for
# the target by build configuration name.
#
attr_reader :specs
attr_reader :specs_by_configuration
# @param [Array<LocalPod>] pods @see pods
# @param [Array<Specification>] pods @see pods
#
def initialize(specs)
@specs = specs
def initialize(specs_by_configuration)
@specs_by_configuration = specs_by_configuration
end
# Generates and saves the file.
......@@ -40,45 +43,131 @@ module Pod
#
# @return [void]
#
def save_as(pathname)
pathname.open('w') do |source|
source.puts
source.puts "// To check if a library is compiled with CocoaPods you"
source.puts "// can use the `COCOAPODS` macro definition which is"
source.puts "// defined in the xcconfigs so it is available in"
source.puts "// headers also when they are imported in the client"
source.puts "// project."
source.puts
source.puts
specs.each do |spec|
spec_name = safe_spec_name(spec.name)
source.puts "// #{spec.name}"
source.puts "#define COCOAPODS_POD_AVAILABLE_#{spec_name}"
if spec.version.semantic?
source.puts "#define COCOAPODS_VERSION_MAJOR_#{spec_name} #{spec.version.major}"
source.puts "#define COCOAPODS_VERSION_MINOR_#{spec_name} #{spec.version.minor}"
source.puts "#define COCOAPODS_VERSION_PATCH_#{spec_name} #{spec.version.patch}"
else
source.puts "// This library does not follow semantic-versioning,"
source.puts "// so we were not able to define version macros."
source.puts "// Please contact the author."
source.puts "// Version: #{spec.version}."
end
source.puts
end
def generate
result = "\n#{notice}\n\n"
common_specs = common_specs(specs_by_configuration)
common_specs.each { |spec| result << spec_defines(spec) }
specs_by_config = specs_scoped_by_configuration(common_specs, specs_by_configuration)
specs_by_config.each do |config, specs|
result << "// #{config} build configuration\n"
result << "#ifdef #{config.gsub(' ', '_').upcase}\n\n"
specs.each { |spec| result << spec_defines(spec, 1) }
result << "#endif\n"
end
result
end
#-----------------------------------------------------------------------#
def save_as(path)
path.open('w') { |header| header.write(generate) }
end
private
# !@group Private Helpers
#-----------------------------------------------------------------------#
# @return [Array<Specification>] The list of the specifications present
# in all build configurations sorted by name.
#
# @param [Hash{String => Array<Specification>}] specs_by_configuration
# The specs grouped by build configuration.
#
def common_specs(specs_by_configuration)
result = specs_by_configuration.values.flatten.uniq
specs_by_configuration.values.each do |configuration_specs|
result = result & configuration_specs
end
result.sort_by(&:name)
end
# @return [Hash{String => Array<Specification>}] The list of the
# specifications not present in all build configurations sorted
# by name and grouped by build configuration name.
#
# @param [Hash{String => Array<Specification>}] specs_by_configuration
# The specs grouped by build configuration.
#
def specs_scoped_by_configuration(common_specs, specs_by_configuration)
result = {}
specs_by_configuration.each do |configuration, all_specs|
specs = all_specs.sort_by(&:name) - common_specs
result[configuration] = specs unless specs.empty?
end
result
end
# @return The sanitized name of a specification to make it suitable to be
# used as part of an identifier of a define statement.
#
# @param [String] spec_name
# The name of the spec.
#
def safe_spec_name(spec_name)
spec_name.gsub(/[^\w]/,'_')
end
# @return [String]
#
def notice
<<-DOC.strip_heredoc
// To check if a library is compiled with CocoaPods you
// can use the `COCOAPODS` macro definition which is
// defined in the xcconfigs so it is available in
// headers also when they are imported in the client
// project.
DOC
end
# @return [String]
#
def spec_defines(spec, indent_count = 0)
spec_name = safe_spec_name(spec.name)
result = "// #{spec.name}\n"
result << "#define COCOAPODS_POD_AVAILABLE_#{spec_name}\n"
if spec.version.semantic?
result << semantic_version_defines(spec)
else
result << non_semantic_version_notice(spec)
end
result << "\n"
indent(result, indent_count)
end
def indent(string, indent_count)
indent = ' ' * (indent_count * 2)
lines = string.lines.map { |line|
if line == "\n"
line
else
"#{indent}#{line}"
end
}
lines.join
end
# @return [String]
#
def semantic_version_defines(spec)
spec_name = safe_spec_name(spec.name)
<<-DOC.strip_heredoc
#define COCOAPODS_VERSION_MAJOR_#{spec_name} #{spec.version.major}
#define COCOAPODS_VERSION_MINOR_#{spec_name} #{spec.version.minor}
#define COCOAPODS_VERSION_PATCH_#{spec_name} #{spec.version.patch}
DOC
end
# @return [String]
#
def non_semantic_version_notice(spec)
<<-DOC.strip_heredoc
// This library does not follow semantic-versioning,
// so we were not able to define version macros.
// Please contact the author.
// Version: #{spec.version}.
DOC
end
#-----------------------------------------------------------------------#
end
......
......@@ -12,8 +12,13 @@ module Pod
# @param [Target] target @see target
#
def initialize(target)
# @param [String] configuration_name
# The name of the build configuration to generate this xcconfig
# for.
#
def initialize(target, configuration_name)
@target = target
@configuration_name = configuration_name
end
# @return [Xcodeproj::Config] The generated xcconfig.
......@@ -54,6 +59,8 @@ module Pod
})
target.pod_targets.each do |pod_target|
next unless pod_target.include_in_build_config?(@configuration_name)
pod_target.file_accessors.each do |file_accessor|
XCConfigHelper.add_spec_build_settings_to_xcconfig(file_accessor.spec_consumer, @xcconfig)
file_accessor.vendored_frameworks.each do |vendored_framework|
......@@ -63,6 +70,13 @@ module Pod
XCConfigHelper.add_library_build_settings(vendored_library, @xcconfig, target.sandbox.root)
end
end
# Add pod static lib to list of libraries that are to be linked with
# the user’s project.
@xcconfig.merge!({
'OTHER_LDFLAGS' => %Q(-l "#{pod_target.name}")
})
end
# TODO Need to decide how we are going to ensure settings like these
......
......@@ -52,7 +52,7 @@ module Pod
'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target),
'PODS_ROOT' => '${SRCROOT}',
'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(search_paths),
'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1',
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1',
# 'USE_HEADERMAP' => 'NO'
}
......
......@@ -97,6 +97,7 @@ module Pod
def resolve_dependencies
UI.section "Analyzing dependencies" do
analyze
validate_build_configurations
prepare_for_legacy_compatibility
clean_sandbox
end
......@@ -117,7 +118,6 @@ module Pod
install_file_references
install_libraries
set_target_dependencies
link_aggregate_target
run_podfile_post_install_hooks
write_pod_project
write_lockfiles
......@@ -178,6 +178,23 @@ module Pod
@aggregate_targets = analyzer.result.targets
end
# Ensures that the white-listed build configurations are known to prevent
# silent typos.
#
# @raise If a unknown user configuration is found.
#
def validate_build_configurations
whitelisted_configs = pod_targets.map do |target|
target.target_definition.all_whitelisted_configurations.map(&:downcase)
end.flatten.uniq
all_user_configurations = analysis_result.all_user_build_configurations.keys.map(&:downcase)
remainder = whitelisted_configs - all_user_configurations
unless remainder.empty?
raise Informative, "Unknown #{'configuration'.pluralize(remainder.size)} whitelisted: #{remainder.sort.to_sentence}."
end
end
# Prepares the Pods folder in order to be compatible with the most recent
# version of CocoaPods.
#
......@@ -424,21 +441,6 @@ module Pod
end
end
# Links the aggregate targets with all the dependent libraries.
#
# @note This is run in the integration step to ensure that targets
# have been created for all per spec libraries.
#
def link_aggregate_target
aggregate_targets.each do |aggregate_target|
native_target = aggregate_target.target
aggregate_target.pod_targets.each do |pod_target|
product = pod_target.target.product_reference
native_target.frameworks_build_phase.add_file_reference(product)
end
end
end
# Writes the Pods project to the disk.
#
# @return [void]
......
......@@ -200,7 +200,7 @@ module Pod
else
target.client_root = config.installation_root
target.user_target_uuids = []
target.user_build_configurations = {}
target.user_build_configurations = target_definition.build_configurations || {}
if target_definition.platform.name == :osx
target.archs = '$(ARCHS_STANDARD_64_BIT)'
end
......@@ -443,9 +443,7 @@ module Pod
def compute_user_build_configurations(target_definition, user_targets)
if user_targets
user_targets.map { |t| t.build_configurations.map(&:name) }.flatten.inject({}) do |hash, name|
unless name == 'Debug' || name == 'Release'
hash[name] = :release
end
hash[name] = name == 'Debug' ? :debug : :release
hash
end.merge(target_definition.build_configurations || {})
else
......
......@@ -32,15 +32,14 @@ module Pod
# @return [void]
#
def create_xcconfig_file
path = library.xcconfig_path
UI.message "- Generating xcconfig file at #{UI.path(path)}" do
gen = Generator::XCConfig::AggregateXCConfig.new(library)
target.build_configurations.each do |configuration|
path = library.xcconfig_path(configuration.name)
UI.message "- Generating #{configuration.name} xcconfig file at #{UI.path(path)}" do
gen = Generator::XCConfig::AggregateXCConfig.new(library, configuration.name)
gen.save_as(path)
library.xcconfig = gen.xcconfig
library.xcconfigs[configuration.name] = gen.xcconfig
xcconfig_file_ref = add_file_to_support_group(path)
target.build_configurations.each do |c|
c.base_configuration_reference = xcconfig_file_ref
configuration.base_configuration_reference = xcconfig_file_ref
end
end
end
......@@ -51,7 +50,7 @@ module Pod
def create_target_environment_header
path = library.target_environment_header_path
UI.message "- Generating target environment header at #{UI.path(path)}" do
generator = Generator::TargetEnvironmentHeader.new(library.pod_targets.map { |l| l.specs }.flatten)
generator = Generator::TargetEnvironmentHeader.new(library.specs_by_build_configuration)
generator.save_as(path)
add_file_to_support_group(path)
end
......
......@@ -62,6 +62,7 @@ module Pod
create_workspace
integrate_user_targets
warn_about_empty_podfile
warn_about_xcconfig_overrides
end
#-----------------------------------------------------------------------#
......@@ -133,6 +134,28 @@ module Pod
end
end
# Checks whether the settings of the CocoaPods generated xcconfig are
# overridden by the build configuration of a target and prints a
# warning to inform the user if needed.
#
def warn_about_xcconfig_overrides
targets.each do |aggregate_target|
aggregate_target.user_targets.each do |user_target|
user_target.build_configurations.each do |config|
xcconfig = aggregate_target.xcconfigs[config.name]
if xcconfig
xcconfig.to_hash.keys.each do |key|
target_value = config.build_settings[key]
if target_value && !target_value.include?('$(inherited)')
print_override_warning(aggregate_target, user_target, config, key)
end
end
end
end
end
end
end
private
# @!group Private Helpers
......@@ -169,11 +192,37 @@ module Pod
end.compact.uniq
end
def targets_to_integrate
targets.reject { |target| target.target_definition.empty? }
end
# Prints a warning informing the user that a build configuration of
# the integrated target is overriding the CocoaPods build settings.
#
# @param [Target::AggregateTarget] aggregate_target
# The umbrella target.
#
# @param [XcodeProj::PBXNativeTarget] user_target
# The native target.
#
# @param [Xcodeproj::XCBuildConfiguration] config
# The build configuration.
#
# @param [String] key
# The key of the overridden build setting.
#
def print_override_warning(aggregate_target, user_target, config, key)
actions = [
"Use the `$(inherited)` flag, or",
"Remove the build settings from the target."
]
message = "The `#{user_target.name} [#{config.name}]` " \
"target overrides the `#{key}` build setting defined in " \
"`#{aggregate_target.xcconfig_relative_path(config.name)}'. " \
"This can lead to problems with the CocoaPods installation"
UI.warn(message, actions)
end
#-----------------------------------------------------------------------#
end
......
......@@ -9,6 +9,8 @@ module Pod
#
class TargetIntegrator
autoload :XCConfigIntegrator, 'cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator'
# @return [Target] the target that should be integrated.
#
attr_reader :target
......@@ -26,54 +28,15 @@ module Pod
# @return [void]
#
def integrate!
return if native_targets.empty?
UI.section(integration_message) do
add_xcconfig_base_configuration
XCConfigIntegrator.integrate(target, native_targets)
unless native_targets_to_integrate.empty?
add_pods_library
add_copy_resources_script_phase
add_check_manifest_lock_script_phase
user_project.save
end
end
# @return [Array<PBXNativeTarget>] the user targets for integration.
#
def native_targets
unless @native_targets
target_uuids = target.user_target_uuids
native_targets = target_uuids.map do |uuid|
native_target = user_project.objects_by_uuid[uuid]
unless native_target
raise Informative, "[Bug] Unable to find the target with " \
"the `#{uuid}` UUID for the `#{target}` integration library"
end
native_target
end
non_integrated = native_targets.reject do |native_target|
native_target.frameworks_build_phase.files.any? do |build_file|
file_ref = build_file.file_ref
file_ref &&
file_ref.isa == 'PBXFileReference' &&
file_ref.display_name == target.product_name
end
end
@native_targets = non_integrated
end
@native_targets
end
# Read the project from the disk to ensure that it is up to date as
# other TargetIntegrators might have modified it.
#
def user_project
@user_project ||= Xcodeproj::Project.open(target.user_project_path)
user_project.save
end
# Read the pods project from the disk to ensure that it is up to date as
# other TargetIntegrators might have modified it.
#
def pods_project
@pods_project ||= Xcodeproj::Project.open(target.sandbox.project_path)
end
# @return [String] a string representation suitable for debugging.
......@@ -82,41 +45,11 @@ module Pod
"#<#{self.class} for target `#{target.label}'>"
end
#---------------------------------------------------------------------#
# @!group Integration steps
private
# @return [Specification::Consumer] the consumer for the specifications.
#
def spec_consumers
@spec_consumers ||= target.pod_targets.map(&:file_accessors).flatten.map(&:spec_consumer)
end
# Adds the `xcconfig` configurations files generated for the current
# {TargetDefinition} to the build configurations of the targets that
# should be integrated.
#
# @note It also checks if any build setting of the build
# configurations overrides the `xcconfig` file and warns the
# user.
#
# @todo If the xcconfig is already set don't override it and inform
# the user.
#
# @return [void]
#
def add_xcconfig_base_configuration
xcconfig = user_project.files.select { |f| f.path == target.xcconfig_relative_path }.first ||
user_project.new_file(target.xcconfig_relative_path)
native_targets.each do |native_target|
check_overridden_build_settings(target.xcconfig, native_target)
native_target.build_configurations.each do |config|
config.base_configuration_reference = xcconfig
end
end
end
# @!group Integration steps
#---------------------------------------------------------------------#
# Adds spec libraries to the frameworks build phase of the
# {TargetDefinition} integration libraries. Adds a file reference to
......@@ -127,7 +60,7 @@ module Pod
#
def add_pods_library
frameworks = user_project.frameworks_group
native_targets.each do |native_target|
native_targets_to_integrate.each do |native_target|
library = frameworks.files.select { |f| f.path == target.product_name }.first ||
frameworks.new_product_ref_for_target(target.name, :static_library)
unless native_target.frameworks_build_phase.files_references.include?(library)
......@@ -144,7 +77,7 @@ module Pod
#
def add_copy_resources_script_phase
phase_name = "Copy Pods Resources"
native_targets.each do |native_target|
native_targets_to_integrate.each do |native_target|
phase = native_target.shell_script_build_phases.select { |bp| bp.name == phase_name }.first ||
native_target.new_shell_script_build_phase(phase_name)
path = target.copy_resources_script_relative_path
......@@ -164,7 +97,7 @@ module Pod
#
def add_check_manifest_lock_script_phase
phase_name = 'Check Pods Manifest.lock'
native_targets.each do |native_target|
native_targets_to_integrate.each do |native_target|
next if native_target.shell_script_build_phases.any? { |phase| phase.name == phase_name }
phase = native_target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
native_target.build_phases.unshift(phase)
......@@ -182,42 +115,47 @@ module Pod
end
end
#---------------------------------------------------------------------#
# @!group Private helpers.
private
# Informs the user about any build setting of the target which might
# override the given xcconfig file.
#
# @return [void]
#
def check_overridden_build_settings(xcconfig, native_target)
return unless xcconfig
# @!group Private helpers
#---------------------------------------------------------------------#
configs_by_overridden_key = {}
native_target.build_configurations.each do |config|
xcconfig.attributes.keys.each do |key|
target_value = config.build_settings[key]
# @return [Array<PBXNativeTarget>] The list of all the targets that
# match the given target.
#
def native_targets
@native_targets ||= target.user_targets(user_project)
end
if target_value && !target_value.include?('$(inherited)')
configs_by_overridden_key[key] ||= []
configs_by_overridden_key[key] << config.name
# @return [Array<PBXNativeTarget>] The list of the targets
# that have not been integrated by past installations
# of
#
def native_targets_to_integrate
unless @native_targets_to_integrate
@native_targets_to_integrate = native_targets.reject do |native_target|
native_target.frameworks_build_phase.files.any? do |build_file|
file_ref = build_file.file_ref
file_ref &&
file_ref.isa == 'PBXFileReference' &&
file_ref.display_name == target.product_name
end
end
configs_by_overridden_key.each do |key, config_names|
name = "#{native_target.name} [#{config_names.join(' - ')}]"
actions = [
"Use the `$(inherited)` flag, or",
"Remove the build settings from the target."
]
UI.warn("The target `#{name}` overrides the `#{key}` build " \
"setting defined in `#{target.xcconfig_relative_path}'.",
actions)
end
@native_targets_to_integrate
end
# Read the project from the disk to ensure that it is up to date as
# other TargetIntegrators might have modified it.
#
def user_project
@user_project ||= Xcodeproj::Project.open(target.user_project_path)
end
# @return [Specification::Consumer] the consumer for the specifications.
#
def spec_consumers
@spec_consumers ||= target.pod_targets.map(&:file_accessors).flatten.map(&:spec_consumer)
end
# @return [String] the message that should be displayed for the target
......@@ -229,9 +167,6 @@ module Pod
"into aggregate target #{target.name} " \
"of project #{UI.path target.user_project_path}."
end
#---------------------------------------------------------------------#
end
end
end
......
module Pod
class Installer
class UserProjectIntegrator
class TargetIntegrator
# Configures an user target to use the CocoaPods xcconfigs which allow
# lo link against the Pods.
#
class XCConfigIntegrator
# Integrates the user target.
#
# @param [Target::AggregateTarget] pod_bundle
# The Pods bundle.
#
# @param [Array<PBXNativeTarget>] targets
# The native targets associated which should be integrated
# with the Pod bundle.
#
def self.integrate(pod_bundle, targets)
targets.each do |target|
target.build_configurations.each do |config|
update_from_cocoapods_0_33_1(pod_bundle, targets)
set_target_xcconfig(pod_bundle, config)
end
end
end
private
# @!group Integration steps
#-------------------------------------------------------------------#
# Removes the xcconfig used up to CocoaPods 0.33 from the project and
# deletes the file if it exists.
#
# @param [Target::AggregateTarget] pod_bundle
# The Pods bundle.
#
# @param [XcodeProj::PBXNativeTarget] target
# The native targets.
#
# @todo This can be removed for CocoaPods 1.0
#
def self.update_from_cocoapods_0_33_1(pod_bundle, targets)
targets.map(&:project).uniq.each do |project|
path = pod_bundle.xcconfig_relative_path(nil)
file_ref = project.files.find { |f| f.path == path }
if file_ref
UI.message "- Removing (#{path})" do
file_ref.remove_from_project
absolute_path = pod_bundle.xcconfig_path
File.delete(absolute_path) if File.exist?(absolute_path)
end
end
end
end
# Creates a file reference to the xcconfig generated by
# CocoaPods (if needed) and sets it as the base configuration of
# build configuration of the user target.
#
# @param [Target::AggregateTarget] pod_bundle
# The Pods bundle.
#
# @param [[Xcodeproj::XCBuildConfiguration] config
# The build configuration.
#
def self.set_target_xcconfig(pod_bundle, config)
path = pod_bundle.xcconfig_relative_path(config.name)
file_ref = config.project.files.find { |f| f.path == path }
file_ref ||= config.project.new_file(path)
config.base_configuration_reference = file_ref
end
private
# @!group Private helpers
#-------------------------------------------------------------------#
# Prints a warning informing the user that a build configuration of
# the integrated target is overriding the CocoaPods build settings.
#
# @param [Target::AggregateTarget] pod_bundle
# The Pods bundle.
#
# @param [XcodeProj::PBXNativeTarget] target
# The native target.
#
# @param [Xcodeproj::XCBuildConfiguration] config
# The build configuration.
#
# @param [String] key
# The key of the overridden build setting.
#
def self.print_override_warning(pod_bundle, target, config, key)
actions = [
"Use the `$(inherited)` flag, or",
"Remove the build settings from the target."
]
message = "The `#{target.name} [#{config.name}]` " \
"target overrides the `#{key}` build setting defined in " \
"`#{pod_bundle.xcconfig_relative_path(config.name)}'. " \
"This can lead to problems with the CocoaPods installation"
UI.warn(message, actions)
end
end
end
end
end
end
......@@ -193,6 +193,37 @@ module Pod
podfile_ref
end
# Adds a new build configuration to the project and populates it with
# default settings according to the provided type.
#
# @note This method extends the original Xcodeproj implementation to
# include a preprocessor definition named after the build
# setting. This is done to support the TargetEnvironmentHeader
# specification of Pods available only on certain build
# configurations.
#
# @param [String] name
# The name of the build configuration.
#
# @param [Symbol] type
# The type of the build configuration used to populate the build
# settings, must be :debug or :release.
#
# @return [XCBuildConfiguration] The new build configuration.
#
def add_build_configuration(name, type)
build_configuration = super
values = ["#{name.gsub(' ', '_').upcase}=1"]
settings = build_configuration.build_settings
definitions = Array(settings['GCC_PREPROCESSOR_DEFINITIONS'])
values.each do |value|
unless definitions.include?(value)
definitions << value
end
end
settings['GCC_PREPROCESSOR_DEFINITIONS'] = definitions
build_configuration
end
private
......
......@@ -77,11 +77,19 @@ module Pod
@sandbox.library_support_files_dir(name)
end
# @param [String] variant
# The variant of the xcconfig. Used to differentiate build
# configurations.
#
# @return [Pathname] the absolute path of the xcconfig file.
#
def xcconfig_path
def xcconfig_path(variant = nil)
if variant
support_files_root + "#{label}.#{variant.downcase}.xcconfig"
else
support_files_root + "#{label}.xcconfig"
end
end
# @return [Pathname] the absolute path of the private xcconfig file.
#
......
......@@ -13,6 +13,7 @@ module Pod
@sandbox = sandbox
@pod_targets = []
@file_accessors = []
@xcconfigs = {}
end
# @return [String] the label for the target.
......@@ -37,20 +38,37 @@ module Pod
attr_accessor :user_project_path
# @return [Array<String>] the list of the UUIDs of the user targets that
# will be integrated by this target as identified by the analizer.
# will be integrated by this target as identified by the analyzer.
#
# @note The target instances are not stored to prevent editing different
# instances.
#
attr_accessor :user_target_uuids
# @return [Xcodeproj::Config] The configuration file of the target.
# @return [Array<PBXNativeTarget>] The list of all the user targets that
# will be integrated by this target.
#
# @note The configuration is generated by the {TargetInstaller} and
def user_targets(project = nil)
project ||= Xcodeproj::Project.open(user_project_path)
user_target_uuids.map do |uuid|
native_target = project.objects_by_uuid[uuid]
unless native_target
raise Informative, "[Bug] Unable to find the target with " \
"the `#{uuid}` UUID for the `#{self}` integration library"
end
native_target
end
end
# @return [Hash<String, Xcodeproj::Config>] Map from configuration name to
# configuration file for the target
#
# @note The configurations are generated by the {TargetInstaller} and
# used by {UserProjectIntegrator} to check for any overridden
# values.
#
attr_accessor :xcconfig
attr_reader :xcconfigs
# @return [Array<PodTarget>] The dependencies for this target.
#
......@@ -62,6 +80,19 @@ module Pod
pod_targets.map(&:specs).flatten
end
# @return [Hash{Symbol => Array<PodTarget>}] The pod targets for each
# build configuration.
#
def specs_by_build_configuration
result = {}
user_build_configurations.keys.each do |build_configuration|
result[build_configuration] = pod_targets.select do |pod_target|
pod_target.include_in_build_config?(build_configuration)
end.map(&:specs).flatten
end
result
end
# @return [Array<Specification::Consumer>] The consumers of the Pod.
#
def spec_consumers
......@@ -90,11 +121,12 @@ module Pod
"${SRCROOT}/#{support_files_root.relative_path_from(client_root)}"
end
# @param [String] config_name The build configuration name to get the xcconfig for
# @return [String] The path of the xcconfig file relative to the root of
# the user project.
#
def xcconfig_relative_path
relative_to_srcroot(xcconfig_path).to_s
def xcconfig_relative_path(config_name)
relative_to_srcroot(xcconfig_path(config_name)).to_s
end
# @return [String] The path of the copy resources script relative to the
......
......@@ -64,5 +64,40 @@ module Pod
end.flatten
end
# Checks if the target should be included in the build configuration with
# the given name.
#
# @param [String] configuration_name
# The name of the build configuration.
#
def include_in_build_config?(configuration_name)
whitelists = target_definition_dependencies.map do |dependency|
target_definition.pod_whitelisted_for_configuration?(dependency.name, configuration_name)
end.uniq
if whitelists.empty?
return true
elsif whitelists.count == 1
whitelists.first
else
raise Informative, "The subspecs of `#{pod_name}` are linked to " \
"different build configurations for the `#{target_definition}` " \
"target. CocoaPods does not support subspecs across different " \
"build configurations."
end
end
private
# @return [Array<Dependency>] The dependency of the target definition for
# this Pod. Return an empty array if the Pod is not a direct
# dependency of the target definition but the dependency of one or
# more Pods.
#
def target_definition_dependencies
target_definition.dependencies.select do |dependency|
Specification.root_name(dependency.name) == pod_name
end
end
end
end
Subproject commit 9ff0ddd87c25f7357a70e8863cef47b0e2557eca
Subproject commit 169cd92092a35deaa21330963369b98a1f235264
......@@ -50,7 +50,6 @@ require 'colored'
require "clintegracon"
require "integration/xcodeproj_project_yaml"
CLIntegracon.configure do |c|
c.spec_path = ROOT + "spec/cocoapods-integration-specs"
c.temp_path = ROOT + "tmp"
......@@ -105,7 +104,8 @@ describe_cli "pod" do
subject do |s|
s.executable = "ruby #{ROOT + "bin/pod"}"
s.environment_vars = {
"CP_AGGRESSIVE_CACHE" => "TRUE"
"CP_AGGRESSIVE_CACHE" => "TRUE",
"XCODEPROJ_DISABLE_XCPROJ" => "TRUE",
}
s.default_args = [
"--verbose",
......
......@@ -3,8 +3,8 @@ require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Generator::TargetEnvironmentHeader do
before do
specification = fixture_spec('banana-lib/BananaLib.podspec')
@gen = Pod::Generator::TargetEnvironmentHeader.new([specification])
spec = fixture_spec('banana-lib/BananaLib.podspec')
@gen = Pod::Generator::TargetEnvironmentHeader.new({'Debug' => [spec]})
end
it "generates a header files which include macro definitions for installed Pods" do
......@@ -32,5 +32,52 @@ describe Pod::Generator::TargetEnvironmentHeader do
name = @gen.send(:safe_spec_name, 'AppleCoreAudioUtilityClasses@thehtb')
name.should == 'AppleCoreAudioUtilityClasses_thehtb'
end
it "includes conditional statements for specifications not present in all build configurations" do
spec = fixture_spec('banana-lib/BananaLib.podspec')
debug_spec = stub(:name => 'DebugPod', :version => Pod::Version.new('1.2.3'))
specs_by_configuration = {
'Debug' => [spec, debug_spec],
'Release' => [spec]
}
@gen = Pod::Generator::TargetEnvironmentHeader.new(specs_by_configuration)
@gen.generate.should == <<-EOS.strip_heredoc
// To check if a library is compiled with CocoaPods you
// can use the `COCOAPODS` macro definition which is
// defined in the xcconfigs so it is available in
// headers also when they are imported in the client
// project.
// BananaLib
#define COCOAPODS_POD_AVAILABLE_BananaLib
#define COCOAPODS_VERSION_MAJOR_BananaLib 1
#define COCOAPODS_VERSION_MINOR_BananaLib 0
#define COCOAPODS_VERSION_PATCH_BananaLib 0
// Debug build configuration
#ifdef DEBUG
// DebugPod
#define COCOAPODS_POD_AVAILABLE_DebugPod
#define COCOAPODS_VERSION_MAJOR_DebugPod 1
#define COCOAPODS_VERSION_MINOR_DebugPod 2
#define COCOAPODS_VERSION_PATCH_DebugPod 3
#endif
EOS
end
it "normalizes the name of the build configuration" do
spec = fixture_spec('banana-lib/BananaLib.podspec')
specs_by_configuration = {
'Debug' => [],
'build configuration copy' => [spec]
}
@gen = Pod::Generator::TargetEnvironmentHeader.new(specs_by_configuration)
@gen.generate.should.include 'BUILD_CONFIGURATION_COPY'
end
end
......@@ -9,6 +9,7 @@ module Pod
@spec = fixture_spec('banana-lib/BananaLib.podspec')
@consumer = @spec.consumer(:ios)
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target_definition.whitelist_pod_for_configuration("BananaLib", "Release")
@target = AggregateTarget.new(target_definition, config.sandbox)
@target.client_root = config.sandbox.root.dirname
@target.stubs(:platform).returns(:ios)
......@@ -16,7 +17,7 @@ module Pod
@pod_target.stubs(:platform).returns(:ios)
@pod_target.stubs(:spec_consumers).returns([@consumer])
@target.pod_targets = [@pod_target]
@generator = AggregateXCConfig.new(@target)
@generator = AggregateXCConfig.new(@target, "Release")
end
it "returns the path of the pods root relative to the user project" do
......@@ -73,6 +74,16 @@ module Pod
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include '$(inherited)'
end
it "links the pod targets with the aggregate integration library target" do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l "Pods-BananaLib"'
end
it "does not links the pod targets with the aggregate integration library target for non-whitelisted configuration" do
@generator = AggregateXCConfig.new(@target, "Debug")
@xcconfig = @generator.generate
@xcconfig.to_hash['OTHER_LDFLAGS'].should.not.include '-lPods-BananaLib'
end
#-----------------------------------------------------------------------#
before do
......
......@@ -51,7 +51,8 @@ module Pod
end
it 'adds the COCOAPODS macro definition' do
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include 'COCOAPODS=1'
expected = '$(inherited) COCOAPODS=1'
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should == expected
end
it 'adds the pod namespaced configuration items' do
......
......@@ -36,15 +36,15 @@ module Pod
end
it "includes the libraries for the specifications" do
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include('-lxml2')
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include(%q(-l "xml2"))
end
it "includes the frameworks of the specifications" do
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include('-framework QuartzCore')
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include(%q(-framework "QuartzCore"))
end
it "includes the weak-frameworks of the specifications" do
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include('-weak_framework iAd')
@xcconfig.to_hash["OTHER_LDFLAGS"].should.include(%q(-weak_framework "iAd"))
end
it "includes the developer frameworks search paths when SenTestingKit is detected" do
......
......@@ -57,7 +57,7 @@ module Pod
:platform_name => :ios
})
@sut.add_spec_build_settings_to_xcconfig(consumer, xcconfig)
xcconfig.to_hash['OTHER_LDFLAGS'].should == '-framework SenTestingKit'
xcconfig.to_hash['OTHER_LDFLAGS'].should == %q(-framework "SenTestingKit")
end
it "adds the libraries of the xcconfig" do
......@@ -70,7 +70,7 @@ module Pod
:platform_name => :ios
})
@sut.add_spec_build_settings_to_xcconfig(consumer, xcconfig)
xcconfig.to_hash['OTHER_LDFLAGS'].should == '-lxml2'
xcconfig.to_hash['OTHER_LDFLAGS'].should == %q(-l "xml2")
end
it "adds the frameworks of the xcconfig" do
......@@ -83,7 +83,7 @@ module Pod
:platform_name => :ios
})
@sut.add_spec_build_settings_to_xcconfig(consumer, xcconfig)
xcconfig.to_hash['OTHER_LDFLAGS'].should == '-framework CoreAnimation'
xcconfig.to_hash['OTHER_LDFLAGS'].should == %q(-framework "CoreAnimation")
end
it "adds the weak frameworks of the xcconfig" do
......@@ -96,7 +96,7 @@ module Pod
:platform_name => :ios
})
@sut.add_spec_build_settings_to_xcconfig(consumer, xcconfig)
xcconfig.to_hash['OTHER_LDFLAGS'].should == '-weak_framework iAd'
xcconfig.to_hash['OTHER_LDFLAGS'].should == %q(-weak_framework "iAd")
end
it "adds the ios developer frameworks search paths if needed" do
......@@ -136,7 +136,7 @@ module Pod
xcconfig = Xcodeproj::Config.new
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
hash_config = xcconfig.to_hash
hash_config['OTHER_LDFLAGS'].should == "-framework Parse"
hash_config['OTHER_LDFLAGS'].should == %q(-framework "Parse")
hash_config['FRAMEWORK_SEARCH_PATHS'].should == '"$(PODS_ROOT)/Parse"'
end
......@@ -145,7 +145,7 @@ module Pod
xcconfig = Xcodeproj::Config.new( { 'OTHER_LDFLAGS' => '-framework CoreAnimation' } )
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
hash_config = xcconfig.to_hash
hash_config['OTHER_LDFLAGS'].should == "-framework CoreAnimation -framework Parse"
hash_config['OTHER_LDFLAGS'].should == %q(-framework "CoreAnimation" -framework "Parse")
end
it "doesn't ovverides exiting frameworks search paths" do
......@@ -165,7 +165,7 @@ module Pod
xcconfig = Xcodeproj::Config.new
@sut.add_library_build_settings(path, xcconfig, config.sandbox.root)
hash_config = xcconfig.to_hash
hash_config['OTHER_LDFLAGS'].should == "-lProj4"
hash_config['OTHER_LDFLAGS'].should == %q(-l "Proj4")
hash_config['LIBRARY_SEARCH_PATHS'].should == '"$(PODS_ROOT)/MapBox/Proj4"'
end
end
......
......@@ -89,7 +89,12 @@ module Pod
target.user_target_uuids.should == ["A346496C14F9BE9A0080D870"]
user_proj = Xcodeproj::Project.open(target.user_project_path)
user_proj.objects_by_uuid[target.user_target_uuids.first].name.should == 'SampleProject'
target.user_build_configurations.should == { "Test" => :release, "App Store" => :release }
target.user_build_configurations.should == {
"Debug" => :debug,
"Release" => :release,
"Test" => :release,
"App Store" => :release
}
target.platform.to_s.should == 'iOS 6.0'
end
......@@ -106,7 +111,13 @@ module Pod
it "returns all the configurations the user has in any of its projects and/or targets" do
target_definition = @analyzer.podfile.target_definition_list.first
target_definition.stubs(:build_configurations).returns("AdHoc" => :test)
@analyzer.analyze.all_user_build_configurations.should == { "AdHoc" => :test, "Test" => :release, "App Store" => :release }
@analyzer.analyze.all_user_build_configurations.should == {
"Debug" => :debug,
"Release" => :release,
"AdHoc" => :test,
"Test" => :release,
"App Store" => :release
}
end
#--------------------------------------#
......@@ -315,7 +326,11 @@ module Pod
user_targets = [target]
configurations = @analyzer.send(:compute_user_build_configurations, target_definition, user_targets)
configurations.should == { 'AppStore' => :release }
configurations.should == {
'Debug' => :debug,
'Release' => :release,
'AppStore' => :release
}
end
it "returns the user build configurations specified in the target definition" do
......
......@@ -48,7 +48,10 @@ module Pod
"Pods-dummy.m",
"Pods-environment.h",
"Pods-resources.sh",
"Pods.xcconfig"
"Pods.appstore.xcconfig",
"Pods.debug.xcconfig",
"Pods.release.xcconfig",
"Pods.test.xcconfig"
]
end
......@@ -114,7 +117,7 @@ module Pod
it "creates the xcconfig file" do
@installer.install!
file = config.sandbox.root + @target.xcconfig_path
file = config.sandbox.root + @target.xcconfig_path("Release")
xcconfig = Xcodeproj::Config.new(file)
xcconfig.to_hash['PODS_ROOT'].should == '${SRCROOT}/Pods'
end
......
require File.expand_path('../../../../../spec_helper', __FILE__)
module Pod
describe XCConfigIntegrator = Installer::UserProjectIntegrator::TargetIntegrator::XCConfigIntegrator do
before do
project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@project = Xcodeproj::Project.open(project_path)
Xcodeproj::Project.new(config.sandbox.project_path).save
@target = @project.targets.first
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target_definition.link_with_first_target = true
@pod_bundle = AggregateTarget.new(target_definition, config.sandbox)
@pod_bundle.user_project_path = project_path
@pod_bundle.client_root = project_path.dirname
@pod_bundle.user_target_uuids = [@target.uuid]
configuration = Xcodeproj::Config.new({
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1'
})
@pod_bundle.xcconfigs['Debug'] = configuration
@pod_bundle.xcconfigs['Test'] = configuration
@pod_bundle.xcconfigs['Release'] = configuration
@pod_bundle.xcconfigs['App Store'] = configuration
end
it 'cleans the xcconfig used up to CocoaPods 0.33.1' do
path = @pod_bundle.xcconfig_path
file_ref = @project.new_file(path)
config = @target.build_configuration_list['Release']
config.base_configuration_reference = file_ref
File.expects(:exist?).returns(true)
File.expects(:delete).with(path)
XCConfigIntegrator.integrate(@pod_bundle, [@target])
@project.files.find { |f| f.path == path }.should.be.nil
end
it 'sets the Pods xcconfig as the base config for each build configuration' do
XCConfigIntegrator.integrate(@pod_bundle, [@target])
@target.build_configurations.each do |config|
xcconfig_file = @project.files.find { |f| f.path == @pod_bundle.xcconfig_relative_path(config.name) }
config.base_configuration_reference.should == xcconfig_file
end
end
it 'does not duplicate the file reference to the CocoaPods xcconfig in the user project' do
path = @pod_bundle.xcconfig_relative_path('Release')
existing = @project.new_file(path)
XCConfigIntegrator.integrate(@pod_bundle, [@target])
config = @target.build_configuration_list['Release']
config.base_configuration_reference.should.equal existing
end
end
end
require File.expand_path('../../../../spec_helper', __FILE__)
module Pod
describe TargetIntegrator = Installer::UserProjectIntegrator::TargetIntegrator do
describe "In general" do
# The project contains a `PBXReferenceProxy` in the build files of the
......@@ -11,83 +9,61 @@ module Pod
# the detection of the target.
#
before do
sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@sample_project = Xcodeproj::Project.open(sample_project_path)
project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@project = Xcodeproj::Project.open(project_path)
Xcodeproj::Project.new(config.sandbox.project_path).save
@target = @sample_project.targets.first
@target = @project.targets.first
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target_definition.link_with_first_target = true
@lib = AggregateTarget.new(target_definition, config.sandbox)
@lib.user_project_path = sample_project_path
@lib.client_root = sample_project_path.dirname
@lib.user_target_uuids = [@target.uuid]
@target_integrator = TargetIntegrator.new(@lib)
end
it 'returns the targets that need to be integrated' do
@target_integrator.native_targets.map(&:name).should == %w[ SampleProject ]
end
it 'returns the targets that need to be integrated' do
pods_library = @sample_project.frameworks_group.new_product_ref_for_target('Pods', :static_library)
@target.frameworks_build_phase.add_file_reference(pods_library)
@target_integrator.stubs(:user_project).returns(@sample_project)
@target_integrator.native_targets.map(&:name).should.be.empty?
end
it 'is robust against other types of references in the build files of the frameworks build phase' do
build_file = @sample_project.new(Xcodeproj::Project::PBXBuildFile)
build_file.file_ref = @sample_project.new(Xcodeproj::Project::PBXVariantGroup)
@target_integrator.stubs(:user_project).returns(@sample_project)
@target.frameworks_build_phase.files << build_file
@target_integrator.native_targets.map(&:name).should == %w[ SampleProject ]
end
it 'is robust against build files with missing file references' do
build_file = @sample_project.new(Xcodeproj::Project::PBXBuildFile)
build_file.file_ref = nil
@target_integrator.stubs(:user_project).returns(@sample_project)
@target.frameworks_build_phase.files << build_file
@target_integrator.native_targets.map(&:name).should == %w[ SampleProject ]
end
it 'does not perform the integration if there are no targets to integrate' do
@target_integrator.stubs(:native_targets).returns([])
@target_integrator.expects(:add_xcconfig_base_configuration).never
@target_integrator.expects(:add_pods_library).never
@target_integrator.expects(:add_copy_resources_script_phase).never
@target_integrator.expects(:save_user_project).never
@pod_bundle = AggregateTarget.new(target_definition, config.sandbox)
@pod_bundle.user_project_path = project_path
@pod_bundle.client_root = project_path.dirname
@pod_bundle.user_target_uuids = [@target.uuid]
configuration = Xcodeproj::Config.new({
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1'
})
@pod_bundle.xcconfigs['Debug'] = configuration
@pod_bundle.xcconfigs['Test'] = configuration
@pod_bundle.xcconfigs['Release'] = configuration
@pod_bundle.xcconfigs['App Store'] = configuration
@target_integrator = TargetIntegrator.new(@pod_bundle)
end
describe '#integrate!' do
it 'set the CocoaPods xcconfigs' do
TargetIntegrator::XCConfigIntegrator.expects(:integrate).with(@pod_bundle, [@target])
@target_integrator.integrate!
end
before do
it 'allows the xcconfig integrator to edit already integrated targets if needed' do
@target_integrator.stubs(:native_targets_to_integrate).returns([])
TargetIntegrator::XCConfigIntegrator.expects(:integrate).with(@pod_bundle, [@target])
@target_integrator.integrate!
end
it 'sets the Pods xcconfig as the base config for each build configuration' do
xcconfig_file = @sample_project.files.find { |f| f.path == @lib.xcconfig_relative_path }
@target.build_configurations.each do |config|
config.base_configuration_reference.should == xcconfig_file
end
end
it 'adds references to the Pods static libraries to the Frameworks group' do
@target_integrator.user_project["Frameworks/libPods.a"].should.not == nil
@target_integrator.integrate!
@target_integrator.send(:user_project)["Frameworks/libPods.a"].should.not == nil
end
it 'adds the libPods static library to the "Link binary with libraries" build phase of each target' do
target = @target_integrator.native_targets.first
target.frameworks_build_phase.files.find { |f| f.file_ref.path == 'libPods.a'}.should.not == nil
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.frameworks_build_phase
ref = phase.files.find { |f| f.file_ref.path == 'libPods.a'}
ref.should.not.be.nil
end
it 'adds a Copy Pods Resources build phase to each target' do
target = @target_integrator.native_targets.first
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == "Copy Pods Resources" }
phase.shell_script.strip.should == "\"${SRCROOT}/../Pods/Pods-resources.sh\""
end
it 'adds a Check Manifest.lock build phase to each target' do
target = @target_integrator.native_targets.first
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == "Check Pods Manifest.lock" }
phase.shell_script.should == <<-EOS.strip_heredoc
diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
......@@ -101,11 +77,50 @@ module Pod
end
it 'adds the Check Manifest.lock build phase as the first build phase' do
target = @target_integrator.native_targets.first
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
target.build_phases.first
phase = target.build_phases.find { |bp| bp.name == "Check Pods Manifest.lock" }
target.build_phases.first.should.equal? phase
end
it 'does not perform the integration if there are no targets to integrate' do
@target_integrator.stubs(:native_targets_to_integrate).returns([])
@target_integrator.expects(:add_pods_library).never
@target_integrator.expects(:add_copy_resources_script_phase).never
@target_integrator.expects(:save_user_project).never
@target_integrator.integrate!
end
end
describe 'Private helpers' do
it 'returns the native targets associated with the Pod bundle' do
@target_integrator.send(:native_targets).map(&:name).should == %w[ SampleProject ]
end
it 'returns the targets that need to be integrated' do
pods_library = @project.frameworks_group.new_product_ref_for_target('Pods', :static_library)
@target.frameworks_build_phase.add_file_reference(pods_library)
@project.save
@target_integrator.send(:native_targets_to_integrate).map(&:name).should.be.empty
end
it 'is robust against other types of references in the build files of the frameworks build phase' do
build_file = @project.new(Xcodeproj::Project::PBXBuildFile)
build_file.file_ref = @project.new(Xcodeproj::Project::PBXVariantGroup)
@target_integrator.stubs(:user_project).returns(@project)
@target.frameworks_build_phase.files << build_file
@target_integrator.send(:native_targets).map(&:name).should == %w[ SampleProject ]
end
it 'is robust against build files with missing file references' do
build_file = @project.new(Xcodeproj::Project::PBXBuildFile)
build_file.file_ref = nil
@target_integrator.stubs(:user_project).returns(@project)
@target.frameworks_build_phase.files << build_file
@target_integrator.send(:native_targets).map(&:name).should == %w[ SampleProject ]
end
end
end
end
end
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Installer::UserProjectIntegrator do
describe UserProjectIntegrator = Installer::UserProjectIntegrator do
describe "In general" do
......@@ -23,14 +23,18 @@ module Pod
@library.user_project_path = sample_project_path
@library.user_target_uuids = ['A346496C14F9BE9A0080D870']
empty_library = AggregateTarget.new(@podfile.target_definitions[:empty], config.sandbox)
@integrator = Installer::UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@library, empty_library])
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@library, empty_library])
end
#-----------------------------------------------------------------------#
describe "In general" do
before do
@integrator.stubs(:warn_about_xcconfig_overrides)
end
it "adds the Pods project to the workspace" do
UserProjectIntegrator::TargetIntegrator.any_instance.stubs(:integrate!)
@integrator.integrate!
workspace_path = @integrator.send(:workspace_path)
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
......@@ -41,10 +45,8 @@ module Pod
end
it "integrates the user targets" do
UserProjectIntegrator::TargetIntegrator.any_instance.expects(:integrate!)
@integrator.integrate!
user_project = Xcodeproj::Project.open(@sample_project_path)
target = user_project.objects_by_uuid[@library.user_target_uuids.first]
target.frameworks_build_phase.files.map(&:display_name).should.include('libPods.a')
end
it "warns if the podfile does not contain any dependency" do
......@@ -53,6 +55,35 @@ module Pod
UI.warnings.should.include?('The Podfile does not contain any dependencies')
end
it 'check that the integrated target does not override the CocoaPods build settings' do
UI.warnings = ''
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => 'FLAG=1' })
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config])
@library.stubs(:user_targets).returns([user_target])
@library.xcconfigs['Release'] = {'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1'}
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@library])
@integrator.unstub(:warn_about_xcconfig_overrides)
@integrator.send(:warn_about_xcconfig_overrides)
UI.warnings.should.include 'The `SampleProject [Release]` target ' \
'overrides the `GCC_PREPROCESSOR_DEFINITIONS` build setting'
end
it 'allows build settings which inherit the settings form the CocoaPods xcconfig' do
UI.warnings = ''
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => 'FLAG=1 $(inherited)' })
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config])
@library.stubs(:user_targets).returns([user_target])
@library.xcconfigs['Release'] = {'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1'}
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@library])
@integrator.unstub(:warn_about_xcconfig_overrides)
@integrator.send(:warn_about_xcconfig_overrides)
UI.warnings.should.not.include 'GCC_PREPROCESSOR_DEFINITIONS'
end
end
#-----------------------------------------------------------------------#
......
......@@ -149,6 +149,28 @@ module Pod
#--------------------------------------#
describe "#validate_whitelisted_configurations" do
it "raises when a whitelisted configuration doesn’t exist in the user's project" do
target_definition = @installer.podfile.target_definitions.values.first
target_definition.whitelist_pod_for_configuration('JSONKit', 'YOLO')
@installer.send(:analyze)
should.raise Informative do
@installer.send(:validate_build_configurations)
end
end
it "does not raise if all whitelisted configurations exist in the user's project" do
target_definition = @installer.podfile.target_definitions.values.first
target_definition.whitelist_pod_for_configuration('JSONKit', 'Test')
@installer.send(:analyze)
should.not.raise do
@installer.send(:validate_build_configurations)
end
end
end
#--------------------------------------#
describe "#clean_sandbox" do
before do
......@@ -444,30 +466,6 @@ module Pod
describe "Integrating client projects" do
it "links the pod targets with the aggregate integration library target" do
spec = fixture_spec('banana-lib/BananaLib.podspec')
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target = AggregateTarget.new(target_definition, config.sandbox)
lib_definition = Podfile::TargetDefinition.new('BananaLib', nil)
lib_definition.store_pod('BananaLib')
pod_target = PodTarget.new([spec], lib_definition, config.sandbox)
target.pod_targets = [pod_target]
project = Xcodeproj::Project.new('path')
pods_target = project.new_target(:static_library, target.name, :ios)
target.target = pods_target
native_target = project.new_target(:static_library, pod_target.name, :ios)
pod_target.target = pods_target
@installer.stubs(:pods_project).returns(project)
@installer.stubs(:aggregate_targets).returns([target])
@installer.stubs(:pod_targets).returns([pod_target])
@installer.send(:link_aggregate_target)
pods_target.frameworks_build_phase.files.map(&:file_ref).should.include?(pod_target.target.product_reference)
end
it "integrates the client projects" do
@installer.stubs(:aggregate_targets).returns([AggregateTarget.new(nil, config.sandbox)])
Installer::UserProjectIntegrator.any_instance.expects(:integrate!)
......
......@@ -36,7 +36,7 @@ module Pod
end
it "returns the absolute path of the xcconfig file" do
@lib.xcconfig_path.to_s.should.include?('Pods/Pods.xcconfig')
@lib.xcconfig_path("Release").to_s.should.include?('Pods/Pods.release.xcconfig')
end
it "returns the absolute path of the resources script" do
......@@ -66,7 +66,7 @@ module Pod
end
it "returns the path of the xcconfig file relative to the user project" do
@lib.xcconfig_relative_path.should == 'Pods/Pods.xcconfig'
@lib.xcconfig_relative_path("Release").should == 'Pods/Pods.release.xcconfig'
end
end
......
......@@ -225,6 +225,36 @@ module Pod
f.path.should == '../Podfile'
end
#----------------------------------------#
describe "#add_build_configuration" do
it "adds a preprocessor definition for build configurations" do
configuration = @project.add_build_configuration('Release', :release)
settings = configuration.build_settings
settings['GCC_PREPROCESSOR_DEFINITIONS'].should.include('RELEASE=1')
end
it "doesn't duplicate values" do
original = @project.build_configuration_list['Debug']
original_settings = original.build_settings
original_settings['GCC_PREPROCESSOR_DEFINITIONS'].should ==
["DEBUG=1", "$(inherited)"]
configuration = @project.add_build_configuration('Debug', :debug)
settings = configuration.build_settings
settings['GCC_PREPROCESSOR_DEFINITIONS'].should ==
["DEBUG=1", "$(inherited)"]
end
it "normalizes the name of the configuration" do
configuration = @project.add_build_configuration(
'My Awesome Configuration', :release)
settings = configuration.build_settings
settings['GCC_PREPROCESSOR_DEFINITIONS'].should ==
["MY_AWESOME_CONFIGURATION=1"]
end
end
end
#-------------------------------------------------------------------------#
......
......@@ -24,6 +24,15 @@ module Pod
it "returns the name of its product" do
@target.product_name.should == 'libPods.a'
end
it 'returns the user targets' do
project_path = SpecHelper.fixture('SampleProject/SampleProject.xcodeproj')
@target.user_project_path = project_path
@target.user_target_uuids = ['A346496C14F9BE9A0080D870']
targets = @target.user_targets
targets.count.should == 1
targets.first.class.should == Xcodeproj::Project::PBXNativeTarget
end
end
describe "Support files" do
......@@ -35,7 +44,7 @@ module Pod
end
it "returns the absolute path of the xcconfig file" do
@target.xcconfig_path.to_s.should.include?('Pods/Pods.xcconfig')
@target.xcconfig_path("Release").to_s.should.include?('Pods/Pods.release.xcconfig')
end
it "returns the absolute path of the resources script" do
......@@ -63,18 +72,38 @@ module Pod
end
it "returns the path of the xcconfig file relative to the user project" do
@target.xcconfig_relative_path.should == 'Pods/Pods.xcconfig'
@target.xcconfig_relative_path("Release").should == 'Pods/Pods.release.xcconfig'
end
end
describe "Pod targets" do
before do
spec = fixture_spec('banana-lib/BananaLib.podspec')
target_definition = Podfile::TargetDefinition.new('Pods', nil)
pod_target = PodTarget.new([spec], target_definition, config.sandbox)
@target = AggregateTarget.new(target_definition, config.sandbox)
@spec = fixture_spec('banana-lib/BananaLib.podspec')
@target_definition = Podfile::TargetDefinition.new('Pods', nil)
@pod_target = PodTarget.new([@spec], @target_definition, config.sandbox)
@target = AggregateTarget.new(@target_definition, config.sandbox)
@target.stubs(:platform).returns(:ios)
@target.pod_targets = [pod_target]
@target.pod_targets = [@pod_target]
end
it "returns pod targets by build configuration" do
pod_target_release = PodTarget.new([@spec], @target_definition, config.sandbox)
pod_target_release.expects(:include_in_build_config?).with("Debug").returns(false)
pod_target_release.expects(:include_in_build_config?).with("Release").returns(true)
@target.pod_targets = [@pod_target, pod_target_release]
@target.user_build_configurations = {
"Debug" => :debug,
"Release" => :release
}
expected = {
"Debug" => @pod_target.specs,
"Release" => (@pod_target.specs + pod_target_release.specs)
}
@target.specs_by_build_configuration.should == expected
end
it "returns the specs of the Pods used by this aggregate target" do
@target.specs.map(&:name).should == ["BananaLib"]
end
it "returns the specs of the Pods used by this aggregate target" do
......
......@@ -37,11 +37,32 @@ module Pod
it "returns the name of the Pods on which this target depends" do
@pod_target.dependencies.should == ["monkey"]
end
it "returns whether it is whitelisted in a build configuration" do
@target_definition.store_pod('BananaLib')
@target_definition.whitelist_pod_for_configuration('BananaLib', 'debug')
@pod_target.include_in_build_config?('Debug').should.be.true
@pod_target.include_in_build_config?('Release').should.be.false
end
it "is whitelisted on all build configurations of it is a dependency of other Pods" do
@pod_target.include_in_build_config?('Debug').should.be.true
@pod_target.include_in_build_config?('Release').should.be.true
end
it "raises if a Pod is whitelisted for different build configurations" do
@target_definition.store_pod('BananaLib')
@target_definition.store_pod('BananaLib/Subspec')
@target_definition.whitelist_pod_for_configuration('BananaLib', 'debug')
should.raise Informative do
@pod_target.include_in_build_config?('release').should.be.true
end.message.should.match /subspecs across different build configurations/
end
end
describe "Support files" do
it "returns the absolute path of the xcconfig file" do
@pod_target.xcconfig_path.to_s.should.include 'Pods/Pods-BananaLib.xcconfig'
@pod_target.xcconfig_path("Release").to_s.should.include 'Pods/Pods-BananaLib.release.xcconfig'
end
it "returns the absolute path of the target header file" do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment