src/mulle-bootstrap-fetch.sh
e3e80f18
 #! /bin/sh
5de2fbd6
 #
 #   Copyright (c) 2015 Nat! - Mulle kybernetiK
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
 #   modification, are permitted provided that the following conditions are met:
 #
 #   Redistributions of source code must retain the above copyright notice, this
 #   list of conditions and the following disclaimer.
 #
 #   Redistributions in binary form must reproduce the above copyright notice,
 #   this list of conditions and the following disclaimer in the documentation
 #   and/or other materials provided with the distribution.
 #
 #   Neither the name of Mulle kybernetiK nor the names of its contributors
 #   may be used to endorse or promote products derived from this software
 #   without specific prior written permission.
 #
 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 #   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 #   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 #   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 #   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 #   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 #   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 #   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 #   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #   POSSIBILITY OF SUCH DAMAGE.
 
a3018cdd
 MULLE_BOOTSTRAP_FETCH_SH="included"
 
5de2fbd6
 #
 # this script installs the proper git clones into "clones"
 # it does not to git subprojects.
 # You can also specify a list of "brew" dependencies. That
 # will be third party libraries, you don't tag or debug
 #
a3018cdd
 
 fetch_usage()
40fe269d
 {
eff55101
    cat <<EOF >&2
 usage:
55a86360
    mulle-bootstrap ${COMMAND} [options] [repositories]
 
    Options
5447bf9a
       -cs   :  check /usr/local for duplicates
751a6002
       -e    :  fetch embedded repositories only
5447bf9a
       -nr   :  ignore .bootstrap folders of fetched repositories
       -u    :  try to update symlinked folders as well (not recommended)
55a86360
 
    install  :  clone or symlink non-exisiting repositories and other resources
    update   :  execute a "pull" in fetched repositories
40fe269d
 
290ab3f7
    You can specify the names of the repositories to update.
40fe269d
 EOF
55a86360
    if [ -d "${CLONESFETCH_SUBDIR}" ]
    then
       echo "Currently available repositories are:"
       (cd "${CLONESFETCH_SUBDIR}" ; ls -1 | sed 's/^/   /')
    fi
a3018cdd
    exit 1
40fe269d
 }
 
 
1e8d5d86
 #
 # Use brews for stuff we don't tag
 #
 install_taps()
 {
    local tap
    local taps
 
    log_fluff "Looking for taps"
 
    taps=`read_fetch_setting "taps" | sort | sort -u`
    if [ "${taps}" != "" ]
    then
       fetch_brew_if_needed
 
       IFS="
 "
       for tap in ${taps}
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
ed336411
          exekutor "${BREW}" tap "${tap}" > /dev/null || exit 1
1e8d5d86
       done
5c06c0e8
       IFS="${DEFAULT_IFS}"
1e8d5d86
    else
       log_fluff "No taps found"
    fi
 }
 
 
ed336411
 
 write_protect_directory()
 {
8f77be37
    if [ -d "$1" ]
ed336411
    then
       #
       # ensure basic structure is there to squelch linker warnings
       #
8f77be37
       exekutor mkdir "$1/Frameworks" 2> /dev/null
       exekutor mkdir "$1/lib" 2> /dev/null
       exekutor mkdir "$1/include" 2> /dev/null
ed336411
 
8f77be37
       log_info "Write-protecting ${C_RESET_BOLD}$1${C_INFO} to avoid spurious header edits"
       exekutor chmod -R a-w "$1"
ed336411
    fi
 }
 
 #
 # brews are now installed using a local brew
 # if we are on linx
 #
1e8d5d86
 install_brews()
 {
b5ffd3fb
    local flag
 
    flag="$1"
 
1e8d5d86
    local brew
    local brews
ed336411
    local brewcmd
1e8d5d86
 
    install_taps
 
    log_fluff "Looking for brews"
 
ed336411
    case "${COMMAND}" in
5e30f645
       fetch)
ed336411
          brewcmd="install"
5e30f645
       ;;
ed336411
       update)
          brewcmd="upgrade"
5e30f645
       ;;
       *)
          fail "COMMAND not set"
       ;;
ed336411
    esac
 
1e8d5d86
    brews=`read_fetch_setting "brews" | sort | sort -u`
ed336411
    if [ -z "${brews}" ]
1e8d5d86
    then
ed336411
       log_fluff "No brews found"
       return
    fi
1e8d5d86
 
ec2b9bc2
    [ -z "${MULLE_BOOTSTRAP_BREW_SH}" ] && . mulle-bootstrap-brew.sh
22174806
 
b5ffd3fb
    if [ "${flag}" = "NO" -a -d "${ADDICTION_SUBDIR}" ]
ed336411
    then
       log_fluff "Unprotecting \"${ADDICTION_SUBDIR}\" for ${command}."
       exekutor chmod -R u+w "${ADDICTION_SUBDIR}"
    fi
 
    local flag
 
    IFS="
1e8d5d86
 "
ed336411
    for formula in ${brews}
    do
5c06c0e8
       IFS="${DEFAULT_IFS}"
50fec60e
 
ed336411
       if [ ! -x "${BREW}" ]
       then
          brew_update_if_needed "${formula}"
          flag=$?
50fec60e
 
ed336411
          if [ $flag -eq 2 ]
          then
             log_info "No brewing being done."
             write_protect_directory "${ADDICTION_SUBDIR}"
             return 1
1e8d5d86
          fi
ed336411
       fi
 
       local versions
 
       versions=""
5e30f645
       if [ "${brewcmd}" = "install" ]
ed336411
       then
          versions="`${BREW} ls --versions "${formula}" 2> /dev/null`"
       fi
 
       if [ -z "${versions}" ]
       then
          log_fluff "brew ${brewcmd} \"${formula}\""
          exekutor "${BREW}" "${brewcmd}" "${formula}" || exit 1
 
          log_info "Force linking it, in case it was keg-only"
          exekutor "${BREW}" link --force "${formula}" || exit 1
       else
b5ffd3fb
          if [ "${flag}" = "NO" ]
          then
             log_info "\"${formula}\" is already installed."
          else
             log_fluff "\"${formula}\" is already installed."
          fi
ed336411
       fi
    done
5c06c0e8
    IFS="${DEFAULT_IFS}"
ed336411
 
b5ffd3fb
    if [ "${flag}" = "YES" ]
    then
       write_protect_directory "${ADDICTION_SUBDIR}"
    fi
1e8d5d86
 }
 
 
 #
 # future, download tarballs...
422c21da
 # we check for existance during fetch, but install during build
fe01cf28
 #
1e8d5d86
 check_tars()
 {
    local tarballs
    local tar
 
    log_fluff "Looking for tarballs"
 
    tarballs="`read_fetch_setting "tarballs" | sort | sort -u`"
    if [ "${tarballs}" != "" ]
    then
       IFS="
 "
       for tar in ${tarballs}
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
 
1e8d5d86
          if [ ! -f "$tar" ]
          then
             fail "tarball \"$tar\" not found"
          fi
          log_fluff "tarball \"$tar\" found"
       done
5c06c0e8
       IFS="${DEFAULT_IFS}"
 
1e8d5d86
    else
       log_fluff "No tarballs found"
    fi
 }
 
 
 #
 ###
 #
5de2fbd6
 link_command()
 {
    local src
    local dst
    local tag
 
    src="$1"
    dst="$2"
    tag="$3"
 
fe01cf28
    local dstdir
a203984b
    dstdir="`dirname -- "${dst}"`"
fe01cf28
 
cce6dc3d
    if [ ! -e "${dstdir}/${src}" -a "${MULLE_EXECUTOR_DRY_RUN}" != "YES" ]
5de2fbd6
    then
70513e67
       fail "${C_RESET}${C_BOLD}${dstdir}/${src}${C_ERROR} does not exist ($PWD)"
5de2fbd6
    fi
 
55a86360
    if [ "${COMMAND}" = "fetch" ]
5de2fbd6
    then
fe01cf28
       #
       # relative paths look nicer, but could fail in more complicated
       # settings, when you symlink something, and that repo has symlinks
       # itself
       #
       if read_yes_no_config_setting "absolute_symlinks" "NO"
       then
          local real
 
          real="`( cd "${dstdir}" ; realpath "${src}")`"
7fdfcd11
          log_fluff "Converted symlink \"${src}\" to \"${real}\""
fe01cf28
          src="${real}"
       fi
 
f146fd2e
       log_info "Symlinking ${C_MAGENTA}${C_BOLD}`basename -- ${src}`${C_INFO} ..."
40fe269d
       exekutor ln -s -f "$src" "$dst" || fail "failed to setup symlink \"$dst\" (to \"$src\")"
36b34def
 
5de2fbd6
       if [ "$tag" != "" ]
       then
fe01cf28
          local name
 
a203984b
          name="`basename -- "${dst}"`"
fe01cf28
          log_warning "tag ${tag} will be ignored, due to symlink" >&2
          log_warning "if you want to checkout this tag do:" >&2
b78c95b8
          log_warning "${C_RESET}${C_BOLD}(cd .repos/${name}; git checkout ${GITFLAGS} \"${tag}\" )${C_WARNING}" >&2
5de2fbd6
       fi
    fi
 
    # when we link, we assume that dependencies are there
 }
 
 
 ask_symlink_it()
 {
    local  clone
 
    clone="$1"
    if [ ! -d "${clone}" ]
    then
fe01cf28
       fail "You need to check out ${clone} yourself, as it's not there."
5de2fbd6
    fi
 
f146fd2e
    #
5de2fbd6
    # check if checked out
f146fd2e
    #
5de2fbd6
    if [ -d "${clone}"/.git ]
    then
f146fd2e
        # if bare repo, we can only clone anyway
602ae7d9
       if git_is_bare_repository "${clone}"
f146fd2e
       then
          log_info "${clone} is a bare git repository. So cloning"
          log_info "is the only way to go."
          return 1
       fi
 
5de2fbd6
       flag=1  # mens clone it
       if [ "${SYMLINK_FORBIDDEN}" != "YES" ]
       then
a75ed0ba
          local prompt
 
          prompt="Should ${clone} be symlinked instead of cloned ?
eff55101
 NO is safe, but you often say YES here."
a75ed0ba
 
          if [ ! -z "${tag}" ]
          then
eff55101
             prompt="${prompt} (Since tag ${tag} is set, NO is more reasonable)"
a75ed0ba
          fi
 
          user_say_yes "$prompt"
5de2fbd6
          flag=$?
       fi
a75ed0ba
 
5de2fbd6
       [ $flag -eq 0 ]
       return $?
    fi
 
    # can only symlink because not a .git repo yet
    if [ "${SYMLINK_FORBIDDEN}" != "YES" ]
    then
fe01cf28
       log_info "${clone} is not a git repository (yet ?)"
       log_info "So symlinking is the only way to go."
5de2fbd6
       return 0
    fi
 
f2a4e24c
    case "${UNAME}" in
       minwgw)
          fail "Can't symlink on $UNAME, as symlinks don't exist"
       ;;
 
       *)
          fail "Can't symlink, as its forbidden by setting \"symlink_forbidden\""
       ;;
    esac
5de2fbd6
 }
 
 
40fe269d
 log_fetch_action()
 {
7fdfcd11
    local url
38e53e42
    local dstdir
40fe269d
 
7fdfcd11
    url="$1"
151d8edd
    dstdir="$2"
40fe269d
 
1e8d5d86
    local info
 
7fdfcd11
    if [ -L "${url}" ]
1e8d5d86
    then
       info=" symlinked "
    else
       info=" "
    fi
 
7fdfcd11
    log_fluff "Perform ${COMMAND}${info}${url} in ${dstdir} ..."
1e8d5d86
 }
 
 
602ae7d9
 search_git_repo_in_parent_directory()
 {
    local name
    local branch
 
    name="$1"
    branch="$2"
 
    local found
 
    if [ ! -z "${branch}" ]
    then
       found="../${name}.${branch}"
       if [ -d "${found}" ]
       then
          echo "${found}"
          return
       fi
    fi
 
    found="../${name}"
    if [ -d "${found}" ]
    then
       echo "${found}"
       return
    fi
 
    found="../${name}.git"
    if [ -d "${found}" ]
    then
       echo "${found}"
       return
    fi
 }
 
 
1e8d5d86
 checkout()
 {
7fdfcd11
    local url
1e8d5d86
    local name
151d8edd
    local dstdir
602ae7d9
    local branch
7fdfcd11
    local tag
bf4c7902
    local scm
1e8d5d86
 
7fdfcd11
    name="$1"
    url="$2"
151d8edd
    dstdir="$3"
602ae7d9
    branch="$4"
    tag="$5"
bf4c7902
    scm="$6"
1e8d5d86
 
151d8edd
    [ ! -z "$name" ]   || internal_fail "name is empty"
7fdfcd11
    [ ! -z "$url" ]    || internal_fail "url is empty"
151d8edd
    [ ! -z "$dstdir" ] || internal_fail "dstdir is empty"
1e8d5d86
 
fe01cf28
    local relative
7fdfcd11
    local name2
1e8d5d86
 
a203984b
    relative="`dirname -- "${dstdir}"`"
fe01cf28
    relative="`compute_relative "${relative}"`"
    if [ ! -z "${relative}" ]
    then
       relative="${relative}/"
    fi
a203984b
    name2="`basename -- "${url}"`"  # only works for git really
1e8d5d86
 
8f77be37
 
7fdfcd11
    local operation
    local map
bf4c7902
    local scmflagsdefault
7fdfcd11
 
bf4c7902
    case "${scm}" in
7fdfcd11
       git|"" )
          operation="git_clone"
602ae7d9
          scmflagsdefault="--recursive"
7fdfcd11
          ;;
       svn)
          operation="svn_checkout"
          ;;
 
       *)
bf4c7902
          fail "unknown scm system ${scm}"
7fdfcd11
          ;;
    esac
 
    local found
    local src
    local script
 
    src="${url}"
1e8d5d86
    script="`find_repo_setting_file "${name}" "bin/${COMMAND}.sh"`"
 
    if [ ! -z "${script}" ]
    then
578dcc01
       fetch__run_script "${script}" "$@"
1e8d5d86
    else
7fdfcd11
       case "${url}" in
1e8d5d86
          /*)
602ae7d9
             if git_is_bare_repository "${url}"
1e8d5d86
             then
602ae7d9
                :
             else
                ask_symlink_it "${src}"
                if [ $? -eq 0 ]
                then
                   operation=link_command
                fi
1e8d5d86
             fi
          ;;
 
          ../*|./*)
602ae7d9
             if git_is_bare_repository "${url}"
1e8d5d86
             then
602ae7d9
                :
             else
                ask_symlink_it "${src}"
                if [ $? -eq 0 ]
                then
                   operation=link_command
                   src="${relative}${url}"
                fi
1e8d5d86
             fi
          ;;
 
          *)
602ae7d9
             found="`search_git_repo_in_parent_directory "${name}" "${branch}"`"
             if [ -z "${found}" ]
1e8d5d86
             then
602ae7d9
                found="`search_git_repo_in_parent_directory "${name2}" "${branch}"`"
1e8d5d86
             fi
 
602ae7d9
             if [ ! -z "${found}" ]
1e8d5d86
             then
a75ed0ba
                user_say_yes "There is a \"${found}\" folder in the parent directory of this project.
 (\"${PWD}\"). Use it ?"
1e8d5d86
                if [ $? -eq 0 ]
                then
7fdfcd11
                   src="${found}"
602ae7d9
 
                   if git_is_bare_repository "${src}"
1e8d5d86
                   then
602ae7d9
                      :
                   else
                      ask_symlink_it "${src}"
                      if [ $? -eq 0 ]
                      then
                         operation=link_command
                         src="${relative}${found}"
                      fi
1e8d5d86
                   fi
                fi
             fi
602ae7d9
 
1e8d5d86
          ;;
       esac
40fe269d
 
57730883
       local scmflags
 
602ae7d9
       scmflags="`read_repo_setting "${name}" "checkout" "${scmflagsdefault}"`"
       "${operation}" "${src}" "${dstdir}" "${branch}" "${tag}" "${scmflags}"
096b64ec
 
d2100287
       warn_scripts_main "${dstdir}/.bootstrap" "${dstdir}" || fail "Ok, aborted"  #sic
40fe269d
    fi
 }
 
1e8d5d86
 
602ae7d9
 ensure_clone_branch_is_correct()
 {
    local dstdir
    local branch
 
8f77be37
    dstdir="$1"
    branch="$2"
602ae7d9
 
    local actual
 
    if [ ! -z "${branch}" ]
    then
       actual="`git_get_branch "${dstdir}"`"
       if [ "${actual}" != "${branch}" ]
       then
          fail "Repository \"${dstdir}\" checked-out branch is \"${actual}\".
 But \"${branch}\" is specified.
 Suggested fix:
    mulle-bootstrap clean dist
    mulle-bootstrap"
       fi
    fi
 }
 
79a901e7
 
 did_clone_repository()
 {
    local name
    local url
    local branch
 
    name="$1"
    url="$2"
    branch="$3"
 
    local dstdir
 
    dstdir="${CLONESFETCH_SUBDIR}/${name}"
578dcc01
    fetch__run_build_settings_script "${name}" "${url}" "${dstdir}" "did-install" "${dstdir}" "${name}"
79a901e7
 }
 
096b64ec
 
5de2fbd6
 #
 # Use git clones for stuff that gets tagged
 # if you specify ../ it will assume you have
 # checked it out yourself, If there is something
 # checked out already it will use it, or ask
3be635de
 # convention: .git suffix == repo to clone
 #          no .git suffix, try to symlink
751a6002
 # return value 0 means: reread repositories, as it may have changed
5de2fbd6
 #
40fe269d
 checkout_repository()
 {
d7f96955
    local name
7fdfcd11
    local url
bf4c7902
    local dstdir
602ae7d9
    local branch
40fe269d
 
7fdfcd11
    name="$1"
    url="$2"
151d8edd
    dstdir="$3"
602ae7d9
    branch="$4"
bf4c7902
 
    local flag
e18eee6f
    local run_script
40fe269d
 
e18eee6f
    run_script=-1
751a6002
 
    stop=1
e18eee6f
 
    if [ -e "${dstdir}" ]
40fe269d
    then
e18eee6f
       ensure_clone_branch_is_correct "${dstdir}" "${branch}"
 
       log_fluff "Repository \"${dstdir}\" already exists"
    else
8f77be37
       if [ "${MULLE_BOOTSTRAP_IGNORE_GRAVEYARD}" != "YES" -a -d "${CLONESFETCH_SUBDIR}/.graveyard/${name}" ]
40fe269d
       then
8f77be37
          log_info "Restoring ${name} from graveyard"
49ce4e18
          exekutor mv "${CLONESFETCH_SUBDIR}/.graveyard/${name}" "${CLONESFETCH_SUBDIR}" || fail "move failed"
8f77be37
          ensure_clone_branch_is_correct "${dstdir}" "${branch}"
       else
          checkout "$@"
e18eee6f
          run_script=0  # yes, run it
a203984b
 
751a6002
          if bootstrap_auto_update "${name}" "${url}" "${dstdir}"
          then
             stop=0
          fi
e18eee6f
       fi
    fi
1e8d5d86
 
e18eee6f
    if [ "${COMMAND}" = "fetch" -a "${DONT_RECURSE}" = "" ]
    then
       local old_bootstrap
8f77be37
 
e18eee6f
       old_bootstrap="${BOOTSTRAP_SUBDIR}"
1e8d5d86
 
e18eee6f
       BOOTSTRAP_SUBDIR="${dstdir}/.bootstrap"
       clone_embedded_repositories "${dstdir}/"
       BOOTSTRAP_SUBDIR="${old_bootstrap}"
    fi
602ae7d9
 
b78c95b8
    if [ $run_script -eq 0 ]
e18eee6f
    then
       fetch__run_build_settings_script "${name}" "${url}" "${dstdir}" "post-${COMMAND}" "$@"
40fe269d
    fi
79a901e7
 
751a6002
    return $stop
40fe269d
 }
 
 
 clone_repository()
5de2fbd6
 {
7fdfcd11
    local name
    local url
602ae7d9
    local branch
bf4c7902
    local scm
40fe269d
 
5fb25a38
    name="$1"
    url="$2"
    branch="$3"
    scm="$4"
40fe269d
 
5de2fbd6
    local tag
151d8edd
    local dstdir
5de2fbd6
 
a3018cdd
    log_verbose "Clone ${name} if needed ..."
38e53e42
 
d7f96955
    tag="`read_repo_setting "${name}" "tag"`" #repo (sic)
7a4ae0c8
    dstdir="${CLONESFETCH_SUBDIR}/${name}"
40fe269d
 
2f8d42a5
    if [ "${CHECK_USR_LOCAL_INCLUDE}" = "YES" ] && has_usr_local_include "${name}"
a75ed0ba
    then
2f8d42a5
       log_info "${C_MAGENTA}${C_BOLD}${name}${C_INFO} is a system library, so not fetching it"
       return 1
a75ed0ba
    fi
602ae7d9
 
751a6002
    local stop
 
    stop=1
b78c95b8
 
2f8d42a5
    log_fetch_action "${url}" "${dstdir}"
7fdfcd11
 
2f8d42a5
    # mark the checkout progress, so that we don't do incomplete fetches and
    # later on happily build
a75ed0ba
 
2f8d42a5
    create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
a75ed0ba
 
2f8d42a5
    if checkout_repository "${name}" "${url}" "${dstdir}" "${branch}" "${tag}" "${scm}"
    then
       stop=0
a75ed0ba
    fi
79a901e7
 
2f8d42a5
    remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 
751a6002
    return $stop
40fe269d
 }
 
 
1e8d5d86
 clone_repositories()
 {
40fe269d
    local clone
75913c0b
    local clones
0109072d
    local fetched
    local match
79a901e7
    local rval
75913c0b
    local stop
7fdfcd11
 
0109072d
    fetched=""
 
b78c95b8
    # __parse_expanded_clone variables
    local name
    local url
    local branch
    local scm
    local tag
 
5de2fbd6
    stop=0
    while [ $stop -eq 0 ]
    do
       stop=1
 
7fdfcd11
       clones="`read_fetch_setting "repositories"`"
5de2fbd6
       if [ "${clones}" != "" ]
       then
40fe269d
          ensure_clones_directory
5de2fbd6
 
7fdfcd11
          IFS="
 "
5de2fbd6
          for clone in ${clones}
          do
5c06c0e8
             IFS="${DEFAULT_IFS}"
0109072d
 
5fb25a38
             clone="`expanded_setting "${clone}"`"
 
0109072d
             # avoid superflous updates
f146fd2e
             match="`echo "${fetched}" | grep -x "${clone}"`"
0109072d
             # could remove prefixes here https:// http://
 
             if [ "${match}" != "${clone}" ]
40fe269d
             then
0109072d
                fetched="${fetched}
 ${clone}"
 
75913c0b
                __parse_expanded_clone "${clone}"
e3bb0c25
 
751a6002
                if clone_repository "${name}" "${url}" "${branch}" "${scm}"
0109072d
                then
                   stop=0
                   break
                fi
40fe269d
             fi
          done
5c06c0e8
          IFS="${DEFAULT_IFS}"
 
40fe269d
       fi
    done
1e8d5d86
 
7fdfcd11
    IFS="
 "
0109072d
    for clone in ${fetched}
1e8d5d86
    do
5c06c0e8
       IFS="${DEFAULT_IFS}"
61fc7238
 
75913c0b
       __parse_clone "${clone}"
bf4c7902
 
602ae7d9
       did_clone_repository "${name}" "${url}" "${branch}"
1e8d5d86
    done
7fdfcd11
 
5c06c0e8
    IFS="${DEFAULT_IFS}"
40fe269d
 }
5de2fbd6
 
 
e18eee6f
 #
6a672446
 # return 0, all cool
 # return 1, is symlinked
 # return 2, .bootstrap/repositories changed
 # return 3, is symlinked and .bootstrap/repositories changed
e18eee6f
 #
40fe269d
 update()
 {
    local name
7fdfcd11
    local url
602ae7d9
    local branch
40fe269d
    local tag
151d8edd
    local dstdir
bf4c7902
    local scm
3ed530a3
 
7fdfcd11
    name="$1"
    url="$2"
151d8edd
    dstdir="$3"
602ae7d9
    branch="$4"
    tag="$5"
bf4c7902
    scm="$6"
3ed530a3
 
7fdfcd11
    [ ! -z "$url" ]           || internal_fail "url is empty"
151d8edd
    exekutor [ -d "$dstdir" ] || internal_fail "dstdir \"${dstdir}\" is wrong ($PWD)"
    [ ! -z "$name" ]          || internal_fail "name is empty"
c6e7156e
 
7fdfcd11
    local operation
 
bf4c7902
    case "${scm}" in
7fdfcd11
       git|"" )
          operation="git_pull"
          ;;
       svn)
          operation="svn_update"
          ;;
       *)
bf4c7902
          fail "unknown scm system ${scm}"
7fdfcd11
          ;;
    esac
 
40fe269d
    local script
6a672446
    local before_r
    local before_e
    local after_r
    local after_e
    local rval
c6e7156e
 
927b4b79
    before_r=`modification_timestamp "${dstdir}/.bootstrap/repositories" 2> /dev/null`
    before_e=`modification_timestamp "${dstdir}/.bootstrap/embedded_repositories" 2> /dev/null`
6a672446
 
    rval=0
6704c86d
    if [ ! -L "${dstdir}" -o "${MULLE_BOOTSTRAP_UPDATE_SYMLINKS}" = "YES" ]
40fe269d
    then
578dcc01
       fetch__run_repo_settings_script "${name}" "${dstdir}" "pre-update" "$@"
d7f96955
 
       script="`find_repo_setting_file "${name}" "bin/update.sh"`"
40fe269d
       if [ ! -z "${script}" ]
       then
578dcc01
          fetch__run_script "${script}" "$@"
40fe269d
       else
602ae7d9
          "${operation}" "${dstdir}" "${branch}" "${tag}"
40fe269d
       fi
3ed530a3
 
578dcc01
       fetch__run_repo_settings_script "${name}" "${dstdir}" "post-update" "$@"
fcfae396
    else
602ae7d9
       ensure_clone_branch_is_correct "${dstdir}" "${branch}"
79a901e7
       log_info "Repository ${C_MAGENTA}${C_BOLD}${name}${C_INFO} exists and is symlinked, so not updated."
6a672446
 
       rval=1
    fi
 
927b4b79
    after_r=`modification_timestamp "${dstdir}/.bootstrap/repositories" 2> /dev/null`
    after_e=`modification_timestamp "${dstdir}/.bootstrap/embedded_repositories" 2> /dev/null`
6a672446
 
58c301d4
    if [ "${before_r}" != "${after_r}" -o "${before_e}" != "${after_e}" ]
6a672446
    then
       rval="`expr "$rval" + 2`"
40fe269d
    fi
58c301d4
 
6a672446
    return "$rval"
40fe269d
 }
 
 
 update_repository()
 {
7fdfcd11
    local name
    local url
602ae7d9
    local branch
58c301d4
    local dstdir
40fe269d
 
7fdfcd11
    name="$1"
    url="$2"
602ae7d9
    branch="$3"
58c301d4
    dstdir="${CLONESFETCH_SUBDIR}/${name}"
40fe269d
 
    local name
    local tag
6a672446
    local rval
 
3c8ec4d2
    tag="`read_repo_setting "${name}" "tag"`" #repo (sic)
40fe269d
 
128e6b71
    exekutor [ -x "${dstdir}" ] || fail "\"${name}\" is not a known repository, check \".boostrap.auto/repositories\""
40fe269d
 
7fdfcd11
    log_fetch_action "${url}" "${dstdir}"
1e8d5d86
 
602ae7d9
    update "${name}" "${url}" "${dstdir}" "${branch}" "${tag}"
6a672446
    rval=$?
fcfae396
    #update will return 1 if repo is symlinked
 
6a672446
    if [ "${DONT_RECURSE}" = "" ]
a203984b
    then
6a672446
       if [ $rval -eq 0 -o $rval -eq 2 ]
       then
          local old_bootstrap
61fc7238
 #      local old_fetch
a203984b
 
6a672446
          old_bootstrap="${BOOTSTRAP_SUBDIR}"
61fc7238
 #      old_fetch="${CLONESFETCH_SUBDIR}"
a203984b
 
6a672446
          BOOTSTRAP_SUBDIR="${dstdir}/.bootstrap"
fcfae396
 #      CLONESFETCH_SUBDIR="${dstdir}/.repos"
7a4ae0c8
 
6a672446
          update_embedded_repositories "${dstdir}/"
fcfae396
 
6a672446
          BOOTSTRAP_SUBDIR="${old_bootstrap}"
61fc7238
 #      CLONESFETCH_SUBDIR="${old_fetch}"
6a672446
       fi
a203984b
    fi
602ae7d9
 
    ensure_clone_branch_is_correct "${dstdir}" "${branch}"
6a672446
    [ $rval -eq 0 -o $rval -eq 2 ]
    return $?
1e8d5d86
 }
 
 
 did_update_repository()
 {
7fdfcd11
    local name
    local url
1e8d5d86
 
7fdfcd11
    name="$1"
    url="$2"
1e8d5d86
 
151d8edd
    local dstdir
1e8d5d86
 
7a4ae0c8
    dstdir="${CLONESFETCH_SUBDIR}/${name}"
40fe269d
 
578dcc01
    fetch__run_build_settings_script "${name}" "${url}" "${dstdir}" "did-update" "${dstdir}" "${name}"
40fe269d
 }
 
 
 #
 # Use git clones for stuff that gets tagged
 # if you specify ../ it will assume you have
 # checked it out yourself, If there is something
 # checked out already it will use it, or ask
 # convention: .git suffix == repo to clone
 #          no .git suffix, try to symlink
 #
 update_repositories()
 {
    local clones
    local clone
1e8d5d86
    local name
40fe269d
    local i
5de2fbd6
 
40fe269d
    if [ $# -ne 0 ]
    then
7fdfcd11
       IFS="
 "
1e8d5d86
       for name in "$@"
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
58c301d4
          create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
             update_repository "${name}" "${CLONESFETCH_SUBDIR}/${name}"
          remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
1e8d5d86
       done
 
7fdfcd11
       IFS="
 "
1e8d5d86
       for name in "$@"
40fe269d
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
58c301d4
          create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
             did_update_repository "${name}" "${CLONESFETCH_SUBDIR}/${name}"
          remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
5c06c0e8
       done
 
       IFS="${DEFAULT_IFS}"
6a672446
       return
    fi
 
83a2eec3
    # __parse_expanded_clone
    local name
    local url
6a672446
    local branch
83a2eec3
    local scm
    local tag
 
58c301d4
    local dstdir
75913c0b
    local match
    local stop
    local updated
6a672446
 
    updated=""
 
    stop=0
    while [ $stop -eq 0 ]
    do
       stop=1
 
7fdfcd11
       clones="`read_fetch_setting "repositories"`"
4aa41e69
       clones="`echo "${clones}" | sed '1!G;h;$!d'`"  # reverse lines
 
40fe269d
       if [ "${clones}" != "" ]
       then
7fdfcd11
          IFS="
 "
40fe269d
          for clone in ${clones}
          do
5c06c0e8
             IFS="${DEFAULT_IFS}"
602ae7d9
 
5fb25a38
             clone="`expanded_setting "${clone}"`"
 
6a672446
             # avoid superflous updates
             match="`echo "${updated}" | grep -x "${clone}"`"
3c8ec4d2
 
83a2eec3
             if [ "${match}" = "${clone}" ]
6a672446
             then
83a2eec3
                continue
             fi
6a672446
 
83a2eec3
             updated="${updated}
 ${clone}"
             __parse_expanded_clone "${clone}"
58c301d4
 
83a2eec3
             dstdir="${CLONESFETCH_SUBDIR}/${name}"
58c301d4
 
83a2eec3
             create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
58c301d4
 
83a2eec3
                if [ -e "${dstdir}" ]
6a672446
                then
83a2eec3
                   update_repository "${name}" "${url}" "${branch}"
                   rval=$?
                else
                   scm="`scm_from_clone "${clone}"`"
                   clone_repository "${name}" "${url}" "${branch}" "${scm}"
                   rval=1
6a672446
                fi
83a2eec3
 
             remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 
             if [ $rval -eq 1 ]
             then
                stop=0
                break
6a672446
             fi
1e8d5d86
          done
5c06c0e8
          IFS="${DEFAULT_IFS}"
1e8d5d86
       fi
6a672446
    done
5de2fbd6
 }
 
 
7be3796e
 append_dir_to_gitignore_if_needed()
 {
 
    case "${1}" in
       "${CLONES_SUBDIR}/"*)
          return 0
       ;;
    esac
 
    local directory
 
73f50c63
    # make it absolute dir for git -> '/' <subdir> '/'
7be3796e
 
    case "$1" in
       /*/)
          directory="$1"
       ;;
73f50c63
 
7be3796e
       /*)
          directory="$1/"
       ;;
73f50c63
 
7be3796e
       */)
          directory="/$1"
       ;;
73f50c63
 
7be3796e
       *)
          directory="/$1/"
       ;;
    esac
 
73f50c63
    local pattern1
    local pattern2
    local pattern3
 
    # also match without trailing slash
    pattern1="`echo "${directory}" | sed 's|\(.*\)/$|\1|'`"
 
    # also match without leading slash
    pattern2="`echo "${directory}" | sed 's|^/\(.*\)|\1|'`"
 
    # also match without leading or trailing slash
    pattern3="`echo "${pattern1}"  | sed 's|^/\(.*\)|\1|'`"
 
    fgrep -s -x -e "${directory}" -e "${pattern1}" -e "${pattern2}" -e "${pattern3}" .gitignore > /dev/null 2>&1
 
7be3796e
    if [ $? -ne 0 ]
    then
73f50c63
       redirect_append_exekutor .gitignore echo "${directory}" || fail "Couldn\'t append to .gitignore"
7be3796e
       log_info "Added \"${directory}\" to \".gitignore\""
    fi
 }
 
 
49ce4e18
 #
 # memorize how we embedded the repository, need URL to identify
 # and the subdir, where it was stored
 #
 # store it inside the possibly recursed dstprefix dependency
 #
 remember_embedded_repository()
 {
    local dstprefix
    local name
    local url
    local subdir
 
    dstprefix="$1"
    name="$2"
    url="$3"
    subdir="$4"
 
    local content
    local embeddeddir
    local content
 
    embeddeddir="${dstprefix}${CLONESFETCH_SUBDIR}/.embedded"
    mkdir_if_missing "${embeddeddir}"
    content="${subdir}
 ${url}"
 
    # dont't use symlinks anymore
    log_fluff "Remember embedded repository \"${name}\" via \"${embeddeddir}/${name}\""
2f8d42a5
 
    # useful for old symlinks from previous version
    if [ -L "${embeddeddir}/${name}" ]
    then
       exekutor rm "${embeddeddir}/${name}"
    fi
    remove_file_if_present "${embeddeddir}/${name}"
    redirect_exekutor "${embeddeddir}/${name}" echo "${content}"
49ce4e18
 }
 
 
8f77be37
 clone_embedded_repository()
 {
    local dstprefix
    local clone
 
    dstprefix="$1"
    clone="$2"
 
    local name
    local url
    local dstdir
e3bb0c25
    local subdir
8f77be37
    local branch
    local tag
    local scm
 
75913c0b
    __parse_embedded_clone "${clone}"
e3bb0c25
 
    dstdir="${dstprefix}${subdir}"
8f77be37
 
    log_fetch_action "${url}" "${dstdir}"
 
    if [ ! -d "${dstdir}" ]
    then
       create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 
       if [ "${MULLE_BOOTSTRAP_IGNORE_GRAVEYARD}" != "YES" -a -d "${CLONESFETCH_SUBDIR}/.embedded/.graveyard/${name}" ]
       then
e3bb0c25
          local parent
 
8f77be37
          log_info "Restoring ${name} from embedded graveyard"
e3bb0c25
          parent="`dirname "${dstdir}"`"
          case "${parent}" in
             .)
             ;;
 
             *)
                mkdir_if_missing "${parent}"
             ;;
          esac
 
8f77be37
          exekutor mv "${CLONESFETCH_SUBDIR}/.embedded/.graveyard/${name}" "${dstdir}" || fail "move failed"
          ensure_clone_branch_is_correct "${dstdir}" "${branch}"
       else
          #
          # embedded_repositories are just cloned, no symlinks,
          #
          local old_forbidden
 
          old_forbidden="${SYMLINK_FORBIDDEN}"
 
          SYMLINK_FORBIDDEN="YES"
          checkout "${name}" "${url}" "${dstdir}" "${branch}" "${tag}" "${scm}"
          SYMLINK_FORBIDDEN="${old_forbidden}"
 
          if read_yes_no_config_setting "update_gitignore" "YES"
          then
             if [ -d .git ]
             then
                append_dir_to_gitignore_if_needed "${dstdir}"
             fi
          fi
 
578dcc01
          fetch__run_build_settings_script "${name}" "${url}" "${dstdir}" "post-${COMMAND}" "$@"
8f77be37
       fi
 
    else
       ensure_clone_branch_is_correct "${dstdir}" "${branch}"
 
       log_fluff "Repository \"${dstdir}\" already exists"
    fi
 
49ce4e18
    #
    # always memorize, even if existed, which could be a clean gone wrong
    #
    remember_embedded_repository "${dstprefix}" "${name}" "${url}" "${subdir}"
 
8f77be37
    remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 }
 
 
 clone_embedded_repositories()
 {
    local dstprefix
 
    dstprefix="$1"
 
    local clones
    local clone
 
    MULLE_BOOTSTRAP_SETTINGS_NO_AUTO="YES"
 
    clones="`read_fetch_setting "embedded_repositories"`"
49ce4e18
    if [ ! -z "${clones}" ]
8f77be37
    then
       IFS="
 "
       for clone in ${clones}
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
8f77be37
 
          clone_embedded_repository "${dstprefix}" "${clone}"
       done
5c06c0e8
       IFS="${DEFAULT_IFS}"
8f77be37
 
       remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
    fi
 
    MULLE_BOOTSTRAP_SETTINGS_NO_AUTO=
 }
 
 
7fdfcd11
 update_embedded_repositories()
fe01cf28
 {
a203984b
    local dstprefix
 
    dstprefix="$1"
 
fe01cf28
    local clones
    local clone
83a2eec3
 
    # __parse_embedded_clone
7fdfcd11
    local name
    local url
602ae7d9
    local branch
213c25c7
    local scm
83a2eec3
    local tag
    local subdir
fe01cf28
 
83a2eec3
    local dstdir
fcfae396
 
83a2eec3
    MULLE_BOOTSTRAP_SETTINGS_NO_AUTO="YES"
7fdfcd11
 
    clones="`read_fetch_setting "embedded_repositories"`"
4aa41e69
    clones="`echo "${clones}" | sed '1!G;h;$!d'`"  # reverse lines
 
83a2eec3
    if [ ! -z "${clones}" ]
fe01cf28
    then
7fdfcd11
       IFS="
 "
fe01cf28
       for clone in ${clones}
       do
5c06c0e8
          IFS="${DEFAULT_IFS}"
5fb25a38
 
75913c0b
          __parse_embedded_clone "${clone}"
fe01cf28
 
83a2eec3
          dstdir="${dstprefix}${subdir}"
          log_fetch_action "${url}" "${dstdir}"
58c301d4
 
83a2eec3
          create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
213c25c7
 
83a2eec3
          if [ -e "${dstdir}" ]
a75ed0ba
          then
83a2eec3
             update "${name}" "${url}" "${dstdir}" "${branch}" "${tag}"
a75ed0ba
          else
83a2eec3
             clone_embedded_repository "${dstprefix}" "${clone}"
a75ed0ba
          fi
83a2eec3
 
          remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
fe01cf28
       done
83a2eec3
 
5c06c0e8
       IFS="${DEFAULT_IFS}"
fe01cf28
    fi
7fdfcd11
 
fcfae396
    MULLE_BOOTSTRAP_SETTINGS_NO_AUTO=
fe01cf28
 }
 
a7b563e1
 
55a86360
 _common_main()
5de2fbd6
 {
751a6002
    #
    # it is useful, that fetch understands build options and
    # ignores them
    #
6704c86d
    while [ $# -ne 0 ]
a3018cdd
    do
       case "$1" in
          -h|-help|--help)
55a86360
             ${USAGE}
a3018cdd
          ;;
 
6704c86d
          -nr|--no-recursion)
             DONT_RECURSE="YES"
          ;;
 
5447bf9a
          -cs|--check-usr-local-include)
             MULLE_BOOTSTRAP_CHECK_USR_LOCAL_INCLUDE="YES"
          ;;
 
751a6002
          -e|--embedded-only)
             EMBEDDED_ONLY="YES"
          ;;
 
6704c86d
          -u|--update-symlinks)
             MULLE_BOOTSTRAP_UPDATE_SYMLINKS="YES"
          ;;
 
5c06c0e8
          # build options with no parameters
          -K|--clean|-k|--no-clean)
751a6002
             if [ -z "${MULLE_BOOTSTRAP_WILL_BUILD}" ]
             then
                log_error "unknown option $1"
                ${USAGE}
             fi
          ;;
 
5c06c0e8
          # build options with no parameters
          -j|--cores|-c|--configuration)
             if [ -z "${MULLE_BOOTSTRAP_WILL_BUILD}" ]
             then
                log_error "unknown option $1"
                ${USAGE}
             fi
 
             if [ $# -eq 1 ]
             then
                log_error "missing parameter to option $1"
                ${USAGE}
             fi
             shift
          ;;
 
a3018cdd
          -*)
             log_error "unknown option $1"
55a86360
             ${USAGE}
a3018cdd
          ;;
 
55a86360
          *)
a3018cdd
             break
          ;;
       esac
 
       shift
    done
 
ec2b9bc2
    [ -z "${MULLE_BOOTSTRAP_LOCAL_ENVIRONMENT_SH}" ] && . mulle-bootstrap-local-environment.sh
096b64ec
    [ -z "${MULLE_BOOTSTRAP_SETTINGS_SH}" ]          && . mulle-bootstrap-settings.sh
8f77be37
 
28558a93
    case "${UNAME}" in
8f77be37
       mingw)
28558a93
          SYMLINK_FORBIDDEN="YES"
       ;;
d7f96955
 
28558a93
       *)
          SYMLINK_FORBIDDEN="`read_config_setting "symlink_forbidden"`"
       ;;
    esac
fe01cf28
 
096b64ec
    [ -z "${MULLE_BOOTSTRAP_SCM_SH}" ]          && . mulle-bootstrap-scm.sh
    [ -z "${MULLE_BOOTSTRAP_SCRIPTS_SH}" ]      && . mulle-bootstrap-scripts.sh
8b9f64f2
    [ -z "${MULLE_BOOTSTRAP_WARN_SCRIPTS_SH}" ] && . mulle-bootstrap-warn-scripts.sh
096b64ec
    [ -z "${MULLE_BOOTSTRAP_AUTO_UPDATE_SH}" ]  && . mulle-bootstrap-auto-update.sh
e3bb0c25
    [ -z "${MULLE_BOOTSTRAP_REPOSITORIES_SH}" ] && . mulle-bootstrap-repositories.sh
5ec25abc
    [ -z "${MULLE_BOOTSTRAP_REFRESH_SH}" ]      && . mulle-bootstrap-refresh.sh
8f77be37
 
a75ed0ba
    #
    # should we check for '/usr/local/include/<name>' and don't fetch if
    # present (somewhat dangerous, because we do not check versions)
    #
83a2eec3
    CHECK_USR_LOCAL_INCLUDE="`read_config_setting "check_usr_local_include" "NO"`"
a75ed0ba
 
55a86360
    if [ "${COMMAND}" = "fetch" ]
40fe269d
    then
1e8d5d86
       if [ $# -ne 0 ]
       then
5c06c0e8
          log_error "Additional parameters not allowed for install"
55a86360
          ${USAGE}
1e8d5d86
       fi
602ae7d9
    fi
 
2f8d42a5
    remove_file_if_present "${CLONESFETCH_SUBDIR}/.refresh_done"
    remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_done"
 
602ae7d9
    #
    # Run prepare scripts if present
    #
55a86360
    if [ "${COMMAND}" = "fetch" ]
602ae7d9
    then
b5ffd3fb
        install_brews NO
ed336411
 #
 # remove these, as they aren't installing locally
 #
 #      install_gems
 #      install_pips
1e8d5d86
 
751a6002
       if [ -z "${EMBEDDED_ONLY}" ]
       then
          clone_repositories
       fi
79a901e7
       clone_embedded_repositories
602ae7d9
 
30941060
       # install brews again, in case we inherited some in the meantime
b5ffd3fb
       install_brews YES
30941060
 
bf4c7902
       check_tars
40fe269d
    else
1fddbf4e
       if dir_has_files "${CLONESFETCH_SUBDIR}"
       then
751a6002
          if [ -z "${EMBEDDED_ONLY}" ]
          then
             update_repositories "$@"
          fi
1fddbf4e
          update_embedded_repositories
       else
          log_info "Nothing to update, fetch first"
 
          return 0
       fi
40fe269d
    fi
5de2fbd6
 
    #
    # Run prepare scripts if present
    #
58c301d4
    create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 
578dcc01
    fetch__run_fetch_settings_script "post-${COMMAND}" "$@"
a7b563e1
 
602ae7d9
    remove_file_if_present "${CLONESFETCH_SUBDIR}/.fetch_update_started"
 
a7b563e1
    if read_yes_no_config_setting "update_gitignore" "YES"
    then
       if [ -d .git ]
       then
          append_dir_to_gitignore_if_needed "${BOOTSTRAP_SUBDIR}.auto"
          append_dir_to_gitignore_if_needed "${BOOTSTRAP_SUBDIR}.local"
          append_dir_to_gitignore_if_needed "${DEPENDENCY_SUBDIR}"
ed336411
          append_dir_to_gitignore_if_needed "${ADDICTION_SUBDIR}"
a7b563e1
          append_dir_to_gitignore_if_needed "${CLONES_SUBDIR}"
       fi
    fi
5ec25abc
 
    refresh_main || exit 1
 
    #
    # if fetch_done is older than refresh_done, we know we should
    # fetch again
    #
    create_file_if_missing "${CLONESFETCH_SUBDIR}/.fetch_done"
55a86360
 }
 
 
 update_main()
 {
    log_fluff "::: update begin :::"
 
    USAGE="fetch_usage"
    COMMAND="update"
    _common_main "$@"
 
    log_fluff "::: update end :::"
 }
 
 
 fetch_main()
 {
    log_fluff "::: fetch begin :::"
 
    USAGE="fetch_usage"
    COMMAND="fetch"
    _common_main "$@"
22174806
 
    log_fluff "::: fetch end :::"
5de2fbd6
 }