Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AAB build and sign support for Android, compile constants support and other improvements #41

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3bd6061
Making sure we getting AndroidManifest file location exactly from con…
i-nikityuk May 25, 2022
3fa3928
Removed double signing of apk file
i-nikityuk May 25, 2022
771c0cb
Merge pull request #1 from inikityuk/bugfix/add_support_for_multiple_…
inikityuk May 26, 2022
9baee4d
Merge pull request #2 from inikityuk/bugfix/if_signing_details_provid…
inikityuk May 26, 2022
5b5c6d2
Added AAB build support
i-nikityuk May 26, 2022
7123e42
Update readme with new "key-password" option
i-nikityuk May 26, 2022
c4bc7b4
Removed "compile_constants" from options - it will be detected automa…
i-nikityuk May 26, 2022
1a2de2f
Added fallback for missing 'AndroidManifest' tag in project file
i-nikityuk May 26, 2022
1b403ff
Merge pull request #3 from inikityuk/feature/android_aab_build_support
inikityuk May 26, 2022
19771c5
Added "provision_profile_uuid" property to specify provisioning profi…
i-nikityuk May 27, 2022
51b599f
Updated readme
i-nikityuk May 27, 2022
2f0c4e0
Merge pull request #4 from inikityuk/feature/ios_added_property_to_sp…
inikityuk May 27, 2022
f81d752
Updated github readme
i-nikityuk May 27, 2022
790ce9b
Merge pull request #5 from inikityuk/feature/update_github_readme
inikityuk May 27, 2022
d2663f8
added ability to create .xcarchive on build for iOS
i-nikityuk Aug 16, 2022
f8a0cfd
Merge pull request #6 from inikityuk/feature/archive_on_build_support
inikityuk Aug 16, 2022
a0db094
Updated readme
i-nikityuk Aug 16, 2022
d9efc23
Merge pull request #7 from inikityuk/feature/archive_on_build_support
inikityuk Aug 16, 2022
e5cf1d1
Updated build command to match VS
i-nikityuk Aug 27, 2023
fa8c0d9
Updated plugin to support NET 6-7 projects
i-nikityuk Sep 12, 2023
2b8d700
Clean up
i-nikityuk Sep 13, 2023
59712a4
Updated README
i-nikityuk Sep 13, 2023
1147922
Merge pull request #8 from inikityuk/feature/NET6_build_support_update
inikityuk Sep 13, 2023
4bf1f3e
Update JDK version selection
i-nikityuk Jun 13, 2024
90b7706
Merge pull request #9 from inikityuk/feature/NET6_build_support_update
inikityuk Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,25 @@

A fastlane component to make Xamarin builds a breeze. Souyuz is now avaialbe as an **Fastlane plugin** see [fastlane-plugin-souyuz](fastlane-plugin-souyuz) for details.

*NOTE: While souyuz should continue working with your existing configuration just fine, consider using the Fastlane plugin.*

## Using MSBuild

Since Version 0.7.0 souyuz is using `msbuild` by default, because according to Xamarin `xbuild` is deprecated and will be removed soon.

This change should not affect you under normal circumstances. Anyway, if you experience any issues there is a new config option `compiler_bin`, where you can easily pass `xbuild` in again.

Usage example:

```ruby
souyuz(
compiler_bin: 'xbuild' # if xbuild is in your $PATH env variable
)
```

## ToDos

* clean up code (!!!)
* replace path concat with `File.join()`
# inikityuk 12/09/2023
* Added support to build NET 7.0 Android / iOS projects (i.e. Xamarin Android / Xamarin iOS)

# inikityuk 16/08/2022
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please extract this portion to a CHANGELOG.md file?

* iOS: added ability to create *.xcarchive on build

# inikityuk 27/05/2022
### Few major changes to "Souyuz" plugin
* Android: added ability to build AAB files (including signing)
* Android: added support to projects with multiple AndroidManifest files (handy if your build configuration using different ones)
* Android: added "key-password" signing property (handy if "store password" doesn't match with "key password")
* Android/iOS: added support for compile constants (handy if you using them as switches in projects)
* iOS: added ability to specify provisioning profile to sign with
* Updated readme with most common properties and basic usage

## How to use this fork
* Add to your "Pluginfile" inside "fastlane" folder one of this lines:
* gem 'fastlane-plugin-souyuz', github: "inikityuk/souyuz" # To pull gem from this fork (instead of official repo)
* gem 'fastlane-plugin-souyuz', path: "/{LOCAL_PATH_TO_PLUGIN}}/souyuz_fastlane_xamarin_plugin" # For local development (if you wish to apply your own modifications)

## Licensing

Expand Down
17 changes: 12 additions & 5 deletions fastlane-plugin-souyuz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ platform :ios do
lane :example do
souyuz(
platform: "ios",
build_configuration: "Release",
plist_path: "./iOS/Info.plist"
build_target: ['Clean','Build'], # OPTIONAL - default to 'Build'
build_platform: "ios-arm64", # OPTIONAL -> One of "ios-arm64 iossimulator-x64 AnyCPU" -> default to iOS:"ios-arm64", Android:"AnyCPU"
build_configuration: "Release", # OPTIONAL -> default to "Release"
plist_path: "./iOS/Info.plist",
provision_profile_uuid: "{PROVISIONING_PROFILE_TO_SIGN_WITH}", # OPTIONAL -> default to Visual Studio configuration
archive_app: true # Create *.xcarchive file, -> default to "false"
)
end
end
Expand All @@ -29,9 +33,12 @@ platform :android do
souyuz(
platform: "android",
build_configuration: "Release",
keystore_path: "{PATH_TO_YOUR_KEYSTORE}",
keystore_alias: "{ALIAS_OF_YOUR_KEYSTORE}",
keystore_password: "{YOUR_SUPER_SECRET_KEYSTORE_PASSWORD}"
build_target: ['Clean','Build'], # OPTIONAL - default to 'Build'
build_platform: "AnyCPU", # OPTIONAL -> One of "ios-arm64 iossimulator-x64 AnyCPU" -> default to iOS:"ios-arm64", Android:"AnyCPU"
keystore_path: "{PATH_TO_YOUR_KEYSTORE}", # OPTIONAL - if not provided Xamarin default keystore will be used
keystore_alias: "{ALIAS_OF_YOUR_KEYSTORE}", # OPTIONAL - if not provided Xamarin default keystore will be used
keystore_password: "{YOUR_SUPER_SECRET_KEYSTORE_PASSWORD}", # OPTIONAL - if not provided Xamarin default keystore will be used
key_password: "{YOUR_SUPER_SECRET_KEY_PASSWORD}" # OPTIONAL - if not provided Xamarin default keystore will be used
)
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/souyuz.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'souyuz/generators/build_command_generator'
require 'souyuz/generators/zipalign_command_generator'
require 'souyuz/generators/apk_sign_command_generator'
require 'souyuz/generators/aab_sign_command_generator'
require 'souyuz/generators/zip_dsym_command_generator'
require 'souyuz/runner'
require 'souyuz/options'
Expand All @@ -24,8 +25,8 @@ class << self

def config=(value)
@config = value
DetectValues.set_additional_default_values
@cache = {}
DetectValues.set_additional_default_values
end
end

Expand Down
56 changes: 38 additions & 18 deletions lib/souyuz/detect_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ def self.set_additional_default_values

detect_output_path doc_csproj
detect_manifest doc_csproj
detect_build_tools
detect_info_plist
detect_assembly_name doc_csproj # we can only do that for android *after* we detected the android manitfest
detect_min_max_sdk doc_csproj # we can only do that for android *after* we detected the android manitfest
detect_compile_constants doc_csproj # all platforms
detect_android_package_format doc_csproj

return config
end
Expand Down Expand Up @@ -63,34 +65,26 @@ def self.detect_output_path(doc_csproj)
return if Souyuz.config[:output_path]

configuration = Souyuz.config[:build_configuration]
platform = Souyuz.config[:build_platform]

doc_node = doc_csproj.xpath("/*[local-name()='Project']/*[local-name()='PropertyGroup'][translate(@*[local-name() = 'Condition'],'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = \" '$(configuration)|$(platform)' == '#{configuration.downcase}|#{platform.downcase}' \"]/*[local-name()='OutputPath']/text()")
output_path = doc_node.text
UI.user_error! 'Not able to find output path automatically, try to specify it via `output_path` parameter.' unless output_path
target_framework_node = doc_csproj.css('PropertyGroup > TargetFramework')
target_framework = target_framework_node.text

output_path = File.join("bin", "#{configuration}", "#{target_framework}")
Souyuz.config[:output_path] = abs_project_path output_path
end

def self.detect_manifest(doc_csproj)
return if Souyuz.config[:manifest_path] or Souyuz.config[:platform] != Platform::ANDROID

doc_node = doc_csproj.css('PropertyGroup > AndroidManifest')
Souyuz.config[:manifest_path] = abs_project_path doc_node.text
end

def self.detect_build_tools
return if Souyuz.config[:buildtools_path]

UI.user_error! "Please ensure that the Android SDK is installed and the ANDROID_HOME variable is set correctly" unless ENV['ANDROID_HOME']
configuration = Souyuz.config[:build_configuration]
platform = "AnyCPU"

# determine latest buildtool version
buildtools = File.join(ENV['ANDROID_HOME'], 'build-tools')
version = Dir.entries(buildtools).sort.last
doc_node = doc_csproj.xpath("/*[local-name()='Project']/*[local-name()='PropertyGroup'][translate(@*[local-name() = 'Condition'],'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = \" '$(configuration)|$(platform)' == '#{configuration.downcase}|#{platform.downcase}' \"]/*[local-name()='AndroidManifest']/text()")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be cleaned up a bit?
It looks like it is somewhat similar to Lines 172 in the same file.


UI.success "Using Buildtools Version: #{version}..."
# For AAB builds: "<AndroidManifest> tag might be missed, so falling back to first available value"
doc_node = doc_csproj.css('PropertyGroup > AndroidManifest').first if doc_node.empty?

Souyuz.config[:buildtools_path] = File.join(buildtools, version)
Souyuz.config[:manifest_path] = abs_project_path doc_node.text
end

def self.detect_info_plist
Expand All @@ -113,6 +107,14 @@ def self.detect_assembly_name(doc_csproj)
end
end

def self.detect_min_max_sdk(doc_csproj)
if Souyuz.config[:platform] == Platform::ANDROID
doc = get_parser_handle Souyuz.config[:manifest_path] # explicitly for this call, no cache needed
Souyuz.cache[:android_min_sdk_version] = doc.xpath('string(//manifest/uses-sdk/@android:minSdkVersion)')
Souyuz.cache[:android_target_sdk_version] = doc.xpath('string(//manifest/uses-sdk/@android:targetSdkVersion)')
end
end

private_class_method

def self.find_file(query, depth)
Expand Down Expand Up @@ -155,5 +157,23 @@ def self.abs_path(path)
path = File.expand_path(path) # absolute dir
path
end

def self.detect_compile_constants(doc_csproj)
configuration = Souyuz.config[:build_configuration]
platform = "AnyCPU"

compile_constants_node = doc_csproj.xpath("/*[local-name()='Project']/*[local-name()='PropertyGroup'][translate(@*[local-name() = 'Condition'],'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = \" '$(configuration)|$(platform)' == '#{configuration.downcase}|#{platform.downcase}' \"]/*[local-name()='DefineConstants']/text()")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Souyuz.cache[:compile_constants] = compile_constants_node.text.gsub(";", " ")
compile_constants_node.text
end

def self.detect_android_package_format(doc_csproj)
configuration = Souyuz.config[:build_configuration]
platform = "AnyCPU"

android_package_format_node = doc_csproj.xpath("/*[local-name()='Project']/*[local-name()='PropertyGroup'][translate(@*[local-name() = 'Condition'],'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = \" '$(configuration)|$(platform)' == '#{configuration.downcase}|#{platform.downcase}' \"]/*[local-name()='AndroidPackageFormat']/text()")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Souyuz.cache[:android_package_format] = android_package_format_node.text
android_package_format_node.text
end
end
end
61 changes: 61 additions & 0 deletions lib/souyuz/generators/aab_sign_command_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module Souyuz
# Responsible for building the aabsigner command
class AabSignCommandGenerator
class << self
def generate
android_package_path = Souyuz.cache[:build_android_package_path]
android_package_dir = File.dirname(android_package_path)
android_package_filename_with_extension = "#{File.basename(android_package_path, ".*")}-signed#{File.extname(android_package_path)}"
Souyuz.cache[:signed_android_package_path] = "#{File.join("#{android_package_dir}", "#{android_package_filename_with_extension}")}"

parts = prefix
parts << detect_jarsigner_executable
parts += options
parts += pipe

parts
end

def detect_jarsigner_executable
java_parent_folder = "/Library/Java/JavaVirtualMachines"
version = Dir.entries(java_parent_folder).sort.last
jarsigner = "#{File.join(java_parent_folder, version, 'Contents/Home/bin/jarsigner')}"
jarsigner
end

def prefix
[""]
end

def options
options = []
options << "--verbose" if $verbose
options << "-keystore \"#{Souyuz.config[:keystore_path]}\""
options << "-storepass \"#{Souyuz.config[:keystore_password]}\""
options << "-keypass \"#{Souyuz.config[:key_password]}\""
options << "-digestalg \"SHA-256\""
options << "-sigalg \"SHA256withRSA\""
options << "-signedjar \"#{Souyuz.cache[:signed_android_package_path]}\" \"#{Souyuz.cache[:build_android_package_path]}\""
options << "\"#{Souyuz.config[:keystore_alias]}\""

options
end

def pipe
pipe = []

pipe
end

# VS does that
# /Library/Java/JavaVirtualMachines/microsoft-11.jdk/Contents/Home/bin/jarsigner
# -keystore "debug.keystore"
# -storepass android
# -keypass android
# -digestalg SHA-256
# -sigalg SHA256withRSA
# -signedjar com.vald.dynamo.aab
# androiddebugkey
end
end
end
36 changes: 32 additions & 4 deletions lib/souyuz/generators/apk_sign_command_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ module Souyuz
class ApkSignCommandGenerator
class << self
def generate
build_apk_path = Souyuz.cache[:build_apk_path]
Souyuz.cache[:signed_apk_path] = "#{build_apk_path}-signed"
android_package_path = Souyuz.cache[:build_android_package_path]
android_package_dir = File.dirname(android_package_path)
android_package_filename_with_extension = "#{File.basename(android_package_path, ".*")}-signed#{File.extname(android_package_path)}"
Souyuz.cache[:signed_android_package_path] = "#{File.join("#{android_package_dir}", "#{android_package_filename_with_extension}")}"

parts = prefix
#parts << detect_java_executable
parts << detect_apksigner_executable
parts += options
parts << Souyuz.cache[:aligned_apk_path]
Expand All @@ -19,8 +22,19 @@ def prefix
[""]
end

def detect_java_executable
java_parent_folder = "/Library/Java/JavaVirtualMachines"
version = Dir.entries(java_parent_folder).sort.last
java = "#{File.join(java_parent_folder, version, 'Contents/Home/bin/java')} -jar"

java
end

def detect_apksigner_executable
apksigner = File.join(Souyuz.config[:buildtools_path], 'apksigner')
microsoft_buildtools = "/Users/#{ENV['USER']}/Library/Developer/Xamarin/android-sdk-macosx/build-tools"
version = Dir.entries(microsoft_buildtools).sort.last

apksigner = "#{File.join(microsoft_buildtools, version, 'apksigner')}"

apksigner
end
Expand All @@ -32,11 +46,25 @@ def options
options << "--ks \"#{Souyuz.config[:keystore_path]}\""
options << "--ks-pass \"pass:#{Souyuz.config[:keystore_password]}\""
options << "--ks-key-alias \"#{Souyuz.config[:keystore_alias]}\""
options << "--out \"#{Souyuz.cache[:signed_apk_path]}\""
options << "--key-pass \"pass:#{Souyuz.config[:key_password]}\""
options << "--min-sdk-version #{Souyuz.cache[:android_min_sdk_version]}"
options << "--max-sdk-version #{Souyuz.cache[:android_target_sdk_version]}"
options << "--out \"#{Souyuz.cache[:signed_android_package_path]}\""

options
end

# VS does that
# /Library/Java/JavaVirtualMachines/microsoft-11.jdk/Contents/Home/bin/java -jar
# /usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.68/tools/apksigner.jar sign
# --ks "debug.keystore"
# --ks-pass pass:android
# --ks-key-alias androiddebugkey
# --key-pass pass:android
# --min-sdk-version 23
# --max-sdk-version 33
# com.vald.dynamo.staging-Signed.apk

def pipe
pipe = []

Expand Down
8 changes: 5 additions & 3 deletions lib/souyuz/generators/build_command_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ def options

options = []
options << config[:extra_build_options] if config[:extra_build_options]
options << "-p:Configuration=#{config[:build_configuration]}" if config[:build_configuration]
options << "-p:Platform=#{config[:build_platform]}" if Souyuz.project.ios? and config[:build_platform]
options << "-c #{config[:build_configuration]}" if config[:build_configuration]
options << "-p:RuntimeIdentifier=#{config[:build_platform]}" if Souyuz.project.ios? and config[:build_platform]
options << "-p:BuildIpa=true" if Souyuz.project.ios?
options << "-p:DefineConstants=\"#{Souyuz.cache[:compile_constants]}\"" if Souyuz.cache[:compile_constants]
if config[:solution_path]
solution_dir = File.dirname(config[:solution_path])
options << "-p:SolutionDir=\"#{solution_dir}/\""
Expand All @@ -44,7 +45,8 @@ def build_targets
def targets
targets = []
targets += build_targets
targets << "-t:SignAndroidPackage" if Souyuz.project.android?
targets << "-t:SignAndroidPackage" if Souyuz.project.android? and !Souyuz.config[:keystore_path] and !Souyuz.config[:keystore_alias] and !Souyuz.config[:keystore_password]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you group those conditions to a helper method for better readability, please?

targets << "-t:Package" if Souyuz.project.android? and Souyuz.config[:keystore_path] and Souyuz.config[:keystore_alias] and Souyuz.config[:keystore_password]

targets
end
Expand Down
27 changes: 20 additions & 7 deletions lib/souyuz/generators/zipalign_command_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ module Souyuz
class ZipalignCommandGenerator
class << self
def generate
build_apk_path = Souyuz.cache[:build_apk_path]
Souyuz.cache[:aligned_apk_path] = "#{build_apk_path}-aligned"
android_package_path = Souyuz.cache[:build_android_package_path]
android_package_dir = File.dirname(android_package_path)
android_package_filename_with_extension = "#{File.basename(android_package_path, ".*")}-aligned#{File.extname(android_package_path)}"
Souyuz.cache[:aligned_apk_path] = "#{File.join("#{android_package_dir}", "#{android_package_filename_with_extension}")}"

parts = prefix
parts << zipalign_apk
parts << detect_zipalign_executable
parts += options
parts << build_apk_path
parts << android_package_path
parts << Souyuz.cache[:aligned_apk_path]
parts += pipe

parts
end

def zipalign_apk
zipalign = File.join(Souyuz.config[:buildtools_path], 'zipalign')
def detect_zipalign_executable
buildtools = Souyuz.config[:buildtools_root_path]
version = Dir.entries(buildtools).sort.last

zipalign = File.join(buildtools, version, 'zipalign')

zipalign
end

def options
options = []
options << "-v" if $verbose
options << "-f"
options << "-p"
options << "-f" # override file
options << "4"

options
Expand All @@ -40,6 +46,13 @@ def pipe

pipe
end

# VS does that
# /Users/inikityuk/Library/Developer/Xamarin/android-sdk-macosx/build-tools/32.0.0/zipalign
# -p
# 4
# "com.vald.dynamo.staging.apk"
# "com.vald.dynamo.staging-Signed.apk"
end
end
end
Loading