2019-06-04 16:50:29 +00:00
#!/usr/bin/env bash
set -Eeu
# This script takes care of generating/updating the maven-inputs.txt file.
# For this, we:
# 1. query the projects in the main gradle project
# 2. loop through each of the projects, querying its dependencies
# 3. add each one to maven-inputs.txt
GIT_ROOT = $( cd " ${ BASH_SOURCE %/* } " && git rev-parse --show-toplevel)
current_dir = $( cd " ${ BASH_SOURCE %/* } " && pwd )
gradle_opts = "--console plain"
tmp_pom_filename = $( mktemp --tmpdir fetch-maven-deps-XXXX.pom)
2019-09-03 10:51:53 +00:00
tmp_mvn_dep_tree_filename = $( mktemp --tmpdir mvn-dep-tree-XXXX.txt)
2019-06-04 16:50:29 +00:00
deps_file_path = $( mktemp --tmpdir fetch-maven-deps-XXXX-deps.txt)
function join_by { local IFS = " $1 " ; shift; echo " $* " ; }
2019-09-03 10:51:53 +00:00
mavenSources = ( \
2019-06-04 16:50:29 +00:00
https://dl.google.com/dl/android/maven2 \
https://jcenter.bintray.com \
2019-09-03 10:51:53 +00:00
https://plugins.gradle.org/m2 \
2019-06-04 16:50:29 +00:00
https://repo.maven.apache.org/maven2 \
https://maven.fabric.io/public \
https://jitpack.io \
)
mavenSourcesSedFilter = $( join_by '|' ${ mavenSources [@] } )
# Converts a URL to a Maven package ID (e.g. https://dl.google.com/dl/android/maven2/android/arch/core/common/1.0.0/common-1.0.0 -> android.arch.core:common:1.0.0)
function getPackageIdFromURL( ) {
local url = " $1 "
local path = $( echo $url | sed -E " s;( $mavenSourcesSedFilter )/(.+);\2; " )
IFS = '/' read -ra tokens <<< " $path "
local groupLength = $(( ${# tokens [@] } - 3 ))
local groupId = ''
for ( ( i = 0; i<$groupLength ; i++) ) ; do
if [ $i -eq 0 ] ; then
groupId = ${ tokens [i] }
else
groupId = " ${ groupId } . ${ tokens [i] } "
fi
done
artifactId = ${ tokens [-3] }
version = " ${ tokens [-2] } "
echo " $groupId : $artifactId : $version "
}
# Formats the components of a Maven package ID as a URL path component (e.g. android/arch/core/common/1.0.0/common-1.0.0)
function getPath( ) {
local tokens = ( " $@ " )
local groupId = ${ tokens [0] }
local artifactId = ${ tokens [1] }
local version = ${ tokens [2] }
groupId = $( echo $groupId | tr '.' '/' )
echo " $groupId / $artifactId / $version / $artifactId - $version "
}
# Tries to download a POM to $tmp_pom_filename given a base URL (also checks for empty files)
function tryGetPOMFromURL( ) {
local url = " $1 "
rm -f $tmp_pom_filename
2019-09-03 10:51:53 +00:00
curl --output $tmp_pom_filename --silent --fail --location " $url .pom " && test -s $tmp_pom_filename
2019-06-04 16:50:29 +00:00
}
# Given the components of a package ID, will loop through known repositories to figure out a source for the package
function determineArtifactUrl( ) {
2019-09-03 10:51:53 +00:00
# Parse dependency ID into components (group ID, artifact ID, version)
IFS = ':' read -ra tokens <<< " $1 "
2019-06-04 16:50:29 +00:00
local groupId = ${ tokens [0] }
2019-09-03 10:51:53 +00:00
[ -z " $groupId " ] && return
2019-06-04 16:50:29 +00:00
local artifactId = ${ tokens [1] }
2019-09-03 10:51:53 +00:00
local version = $( echo " ${ tokens [2] } " | cut -d'@' -f1)
2019-06-04 16:50:29 +00:00
local path = $( getPath " ${ tokens [@] } " )
for mavenSourceUrl in ${ mavenSources [@] } ; do
if tryGetPOMFromURL " $mavenSourceUrl / $path " ; then
echo " $mavenSourceUrl / $path "
return
fi
done
2019-09-03 10:51:53 +00:00
echo "<NOTFOUND>"
2019-06-04 16:50:29 +00:00
}
# Executes a gradle dependencies command and returns the output package IDs
function runGradleDepsCommand( ) {
echo " Computing maven dependencies with \`gradle $1 $gradle_opts \`... " > /dev/stderr
# Add a comment header with the command we're running (useful for debugging)
echo " # $1 "
# Run the gradle command and:
2019-10-21 21:49:45 +00:00
# - remove lines that end with (*) or (n) but don't start with (+)
2019-06-04 16:50:29 +00:00
# - keep only lines that start with \--- or +---
# - remove lines that refer to a project
# - extract the package name and version, ignoring version range indications, such as in `com.google.android.gms:play-services-ads:[15.0.1,16.0.0) -> 15.0.1`
gradle $1 $gradle_opts \
2019-10-21 21:49:45 +00:00
| grep --invert-match -E " ^[^+].+ \([\*n]\) $" \
2019-06-04 16:50:29 +00:00
| grep -e "[\\\+]---" \
| grep --invert-match -e "--- project :" \
| sed -E " s;.*[\\\+]--- ([^ ]+:)(.+ -> )?([^ ]+).* $;\1\3; "
}
function retrieveAdditionalDependencies( ) {
# It is not enough to output the dependencies in deps, we must also ask maven to report
# the dependencies for each individual POM file. Instead of parsing the dependency tree itself though,
# we look at what packages maven downloads from the internet into the local repo,
# which avoids us having to do a deep search, and does not report duplicates
2019-09-03 10:51:53 +00:00
echo -n > $tmp_mvn_dep_tree_filename
mvn dependency:tree -B -Dmaven.repo.local= $mvn_tmp_repo -f " $1 " > $tmp_mvn_dep_tree_filename 2>& 1 || echo -n
local additional_deps = ( $( cat $tmp_mvn_dep_tree_filename \
2019-06-04 16:50:29 +00:00
| grep -E 'Downloaded from [^:]+: [^ ]+\.(pom|jar|aar)' \
| sed -E " s;^\[INFO\] Downloaded from [^:]+: ([^ ]+)\.(pom|jar|aar) .* $;\1; " ) )
2019-09-03 10:51:53 +00:00
local missing_additional_deps = ( $( cat $tmp_mvn_dep_tree_filename \
| grep -E "The POM for .+:.+:(pom|jar):.+ is missing" \
| sed -E " s;^.*The POM for (.+:.+:(pom|jar):.+) is missing.* $;\1; " ) )
2019-06-04 16:50:29 +00:00
for additional_dep_url in ${ additional_deps [@] } ; do
local additional_dep_id = $( getPackageIdFromURL $additional_dep_url )
# See if we already have this dependency in $deps
local alreadyExists = 0
for _dep in ${ deps [@] } ; do
if [ " $additional_dep_id " = " $_dep " ] ; then
alreadyExists = 1
break
fi
done
[ $alreadyExists -eq 0 ] && echo " $additional_dep_url " || continue
done
2019-09-03 10:51:53 +00:00
for additional_dep_id in ${ missing_additional_deps [@] } ; do
# See if we already have this dependency in $deps
local alreadyExists = 0
for _dep in ${ deps [@] } ; do
if [ " $additional_dep_id " = " $_dep " ] ; then
alreadyExists = 1
break
fi
done
if [ $alreadyExists -eq 0 ] ; then
artifactUrl = $( determineArtifactUrl $additional_dep_id )
if [ -z " $artifactUrl " ] ; then
continue
elif [ " $artifactUrl " = "<NOTFOUND>" ] ; then
# Some dependencies don't contain a normal format, so we ignore them (e.g. `com.squareup.okhttp:okhttp:{strictly`)
echo " Failed to determine source of $dep , ignoring... " > /dev/stderr
continue
fi
echo " $artifactUrl "
fi
done
2019-06-04 16:50:29 +00:00
}
mvn_tmp_repo = $( mktemp -d)
2019-09-03 10:51:53 +00:00
trap " rm -rf $mvn_tmp_repo $tmp_pom_filename $deps_file_path $tmp_mvn_dep_tree_filename " ERR EXIT HUP INT
2019-06-04 16:50:29 +00:00
2019-08-06 16:16:51 +00:00
rnModules = $( node ./node_modules/react-native/cli.js config | jq -r '.dependencies | keys | .[]' )
2019-06-04 16:50:29 +00:00
pushd $GIT_ROOT /android > /dev/null
2019-09-03 10:51:53 +00:00
gradleProjects = $( gradle projects $gradle_opts 2>& 1 \
| grep "Project ':" \
| sed -E "s;^.--- Project '\:([@_a-zA-Z0-9\-]+)';\1;" )
2019-08-06 16:16:51 +00:00
projects = ( ${ gradleProjects [@] } ${ rnModules [@] } )
2019-09-03 10:51:53 +00:00
IFS = $'\n' sortedProjects = ( $( sort -u <<< " ${ projects [*] } " ) )
unset IFS
2019-06-04 16:50:29 +00:00
echo -n > $deps_file_path
# TODO: try to limit unnecessary dependencies brought in by passing e.g. `--configuration releaseCompileClasspath` to the `gradle *:dependencies` command
runGradleDepsCommand 'buildEnvironment' >> $deps_file_path
2019-09-03 10:51:53 +00:00
for project in ${ sortedProjects [@] } ; do
runGradleDepsCommand ${ project } :buildEnvironment >> $deps_file_path
runGradleDepsCommand ${ project } :dependencies >> $deps_file_path
2019-06-04 16:50:29 +00:00
done
popd > /dev/null
# Read the deps file into memory, sorting and getting rid of comments, project names and duplicates
IFS = $'\n' deps = ( $( cat $deps_file_path \
| grep --invert-match -E '^#.*$' \
| grep --invert-match -E '^[a-z]+$' \
| grep --invert-match -E '^:?[^:]+$' \
| sort -uV) )
unset IFS
rm -f $deps_file_path
lineCount = ${# deps [@] }
currentLine = 0
pstr = "[=======================================================================]"
echo " Determining URLs for ${# deps [@] } packages... " > /dev/stderr
for dep in ${ deps [@] } ; do
currentLine = $(( $currentLine + 1 ))
pd = $(( $currentLine * 73 / $lineCount ))
printf " \r%3d.%1d%% %. ${ pd } s " $(( $currentLine * 100 / $lineCount )) $(( ( $currentLine * 1000 / $lineCount ) % 10 )) $pstr > /dev/stderr
[ -z " $dep " ] && continue
# Ignore own dependencies (e.g. status-im:status-go:v0.30.0-beta.0)
if [ [ " $dep " = = "status-im:" * ] ] ; then
echo " Ignoring $dep " > /dev/stderr
continue
fi
2019-09-03 10:51:53 +00:00
artifactUrl = $( determineArtifactUrl $dep )
2019-06-04 16:50:29 +00:00
if [ -z " $artifactUrl " ] ; then
2019-09-03 10:51:53 +00:00
continue
elif [ " $artifactUrl " = "<NOTFOUND>" ] ; then
2019-06-04 16:50:29 +00:00
# Some dependencies don't contain a normal format, so we ignore them (e.g. `com.squareup.okhttp:okhttp:{strictly`)
echo " Failed to determine source of $dep , ignoring... " > /dev/stderr
continue
fi
echo " $artifactUrl "
retrieveAdditionalDependencies $tmp_pom_filename
done