From 7f39d488243244a1bba3ce783c84f3fbc3f268ce Mon Sep 17 00:00:00 2001 From: Tomas Chvatal Date: Wed, 20 Apr 2011 10:56:27 +0000 Subject: Introduce git-2 eclass This is next-gen eclass for git using live ebuilds. Complete usage is documented in eclassdoc. Note that for migration some variables have different names so the ebuilds should be doublechecked that nothing will break. Also this eclass define just one phase src_unpack, so no git-2_src_prepare. --- eclass/git-2.eclass | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 eclass/git-2.eclass (limited to 'eclass') diff --git a/eclass/git-2.eclass b/eclass/git-2.eclass new file mode 100644 index 000000000000..289280064e9a --- /dev/null +++ b/eclass/git-2.eclass @@ -0,0 +1,509 @@ +# Copyright 1999-2011 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/eclass/git-2.eclass,v 1.1 2011/04/20 10:56:27 scarabeus Exp $ + +# @ECLASS: git-2.eclass +# @MAINTAINER: +# Tomas Chvatal +# @BLURB: Eclass for fetching and unpacking git repositories. +# @DESCRIPTION: +# Eclass for easing maitenance of live ebuilds using git as remote repository. +# Eclass support working with git submodules and branching. + +# This eclass support all EAPIs +EXPORT_FUNCTIONS src_unpack + +DEPEND="dev-vcs/git" + +# @ECLASS-VARIABLE: EGIT_SOURCEDIR +# @DESCRIPTION: +# This variable specifies destination where the cloned +# data are copied to. +# +# EGIT_SOURCEDIR="${S}" + +# @ECLASS-VARIABLE: EGIT_STORE_DIR +# @DESCRIPTION: +# Storage directory for git sources. +# +# EGIT_STORE_DIR="${DISTDIR}/egit-src" + +# @ECLASS-VARIABLE: EGIT_HAS_SUBMODULES +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty this variable enables support for git submodules in our +# checkout. Also this makes the checkout to be non-bare for now. + +# @ECLASS-VARIABLE: EGIT_OPTIONS +# @DEFAULT_UNSET +# @DESCRIPTION: +# Variable specifying additional options for fetch command. + +# @ECLASS-VARIABLE: EGIT_MASTER +# @DESCRIPTION: +# Variable for specifying master branch. +# Usefull when upstream don't have master branch or name it differently. +# +# EGIT_MASTER="master" + +# @ECLASS-VARIABLE: EGIT_DIR +# @DESCRIPTION: +# Directory where we want to store the git data. +# This should not be overriden unless really required. +# +# EGIT_DIR="${EGIT_STORE_DIR}/${EGIT_REPO_URI##*/}" + +# @ECLASS-VARIABLE: EGIT_REPO_URI +# @REQUIRED +# @DEFAULT_UNSET +# @DESCRIPTION: +# URI for the repository +# e.g. http://foo, git://bar +# +# Support multiple values: +# EGIT_REPO_URI="git://a/b.git http://c/d.git" + +# @ECLASS-VARIABLE: EVCS_OFFLINE +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty this variable prevents performance of any online +# operations. + +# @ECLASS-VARIABLE: EGIT_BRANCH +# @DESCRIPTION: +# Variable containing branch name we want to check out. +# It can be overriden via env using packagename_LIVE_BRANCH +# variable. +# +# EGIT_BRANCH="${EGIT_MASTER}" + +# @ECLASS-VARIABLE: EGIT_COMMIT +# @DESCRIPTION: +# Variable containing commit hash/tag we want to check out. +# It can be overriden via env using packagename_LIVE_COMMIT +# variable. +# +# EGIT_BRANCH="${EGIT_BRANCH}" + +# @ECLASS-VARIABLE: EGIT_REPACK +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty this variable specifies that repository will be repacked to +# save space. However this can take a REALLY LONG time with VERY big +# repositories. + +# @ECLASS-VARIABLE: EGIT_PRUNE +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty this variable enables pruning all loose objects on each fetch. +# This is useful if upstream rewinds and rebases branches often. + +# @ECLASS-VARIABLE: EGIT_NONBARE +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty this variable specifies that all checkouts will be done using +# non bare repositories. This is useful if you can't operate with bare +# checkouts for some reason. + +# @FUNCTION: git-2_init_variables +# @DESCRIPTION: +# Internal function initializing all git variables. +# We define it in function scope so user can define +# all the variables before and after inherit. +git-2_init_variables() { + debug-print-function ${FUNCNAME} "$@" + + local x + + : ${EGIT_SOURCEDIR="${S}"} + + : ${EGIT_STORE_DIR:="${PORTAGE_ACTUAL_DISTDIR-${DISTDIR}}/egit-src"} + + : ${EGIT_HAS_SUBMODULES:=} + + : ${EGIT_OPTIONS:=} + + : ${EGIT_MASTER:=master} + + eval x="\$${PN//[-+]/_}_LIVE_REPO" + EGIT_REPO_URI=${x:-${EGIT_REPO_URI}} + [[ -z ${EGIT_REPO_URI} ]] && die "EGIT_REPO_URI must have some value" + + : ${EVCS_OFFLINE:=} + + eval x="\$${PN//[-+]/_}_LIVE_BRANCH" + [[ -n ${x} ]] && ewarn "QA: using \"${PN//[-+]/_}_LIVE_BRANCH\" variable, you won't get any support" + EGIT_BRANCH=${x:-${EGIT_BRANCH:-${EGIT_MASTER}}} + + eval x="\$${PN//[-+]/_}_LIVE_COMMIT" + [[ -n ${x} ]] && ewarn "QA: using \"${PN//[-+]/_}_LIVE_COMMIT\" variable, you won't get any support" + EGIT_COMMIT=${x:-${EGIT_COMMIT:-${EGIT_BRANCH}}} + + : ${EGIT_REPACK:=} + + : ${EGIT_PRUNE:=} +} + +# @FUNCTION: git-2_submodules +# @DESCRIPTION: +# Internal function wrapping the submodule initialisation and update. +git-2_submodules() { + debug-print-function ${FUNCNAME} "$@" + if [[ -n ${EGIT_HAS_SUBMODULES} ]]; then + if [[ -n ${EVCS_OFFLINE} ]]; then + # for submodules operations we need to be online + debug-print "${FUNCNAME}: not updating submodules in offline mode" + return 1 + fi + + debug-print "${FUNCNAME}: working in \"${1}\"" + pushd "${EGIT_DIR}" > /dev/null + + debug-print "${FUNCNAME}: git submodule init" + git submodule init || die + debug-print "${FUNCNAME}: git submodule sync" + git submodule sync || die + debug-print "${FUNCNAME}: git submodule update" + git submodule update || die + + popd > /dev/null + fi +} + +# @FUNCTION: git-2_branch +# @DESCRIPTION: +# Internal function that changes branch for the repo based on EGIT_COMMIT and +# EGIT_BRANCH variables. +git-2_branch() { + debug-print-function ${FUNCNAME} "$@" + + debug-print "${FUNCNAME}: working in \"${EGIT_SOURCEDIR}\"" + pushd "${EGIT_SOURCEDIR}" > /dev/null + + local branchname=branch-${EGIT_BRANCH} src=origin/${EGIT_BRANCH} + if [[ ${EGIT_COMMIT} != ${EGIT_BRANCH} ]]; then + branchname=tree-${EGIT_COMMIT} + src=${EGIT_COMMIT} + fi + debug-print "${FUNCNAME}: git checkout -b ${branchname} ${src}" + git checkout -b ${branchname} ${src} \ + || die "${FUNCNAME}: changing the branch failed" + + popd > /dev/null + + unset branchname src +} + +# @FUNCTION: git-2_gc +# @DESCRIPTION: +# Internal function running garbage collector on checked out tree. +git-2_gc() { + debug-print-function ${FUNCNAME} "$@" + + pushd "${EGIT_DIR}" > /dev/null + if [[ -n ${EGIT_REPACK} || -n ${EGIT_PRUNE} ]]; then + ebegin "Garbage collecting the repository" + local args + [[ -n ${EGIT_PRUNE} ]] && args='--prune' + debug-print "${FUNCNAME}: git gc ${args}" + git gc ${args} + eend $? + fi + popd > /dev/null +} + +# @FUNCTION: git-2_prepare_storedir +# @DESCRIPTION: +# Internal function preparing directory where we are going to store SCM +# repository. +git-2_prepare_storedir() { + debug-print-function ${FUNCNAME} "$@" + + local clone_dir + + # initial clone, we have to create master git storage directory and play + # nicely with sandbox + if [[ ! -d ${EGIT_STORE_DIR} ]]; then + debug-print "${FUNCNAME}: Creating git main storage directory" + addwrite / + mkdir -p "${EGIT_STORE_DIR}" \ + || die "${FUNCNAME}: can't mkdir \"${EGIT_STORE_DIR}\"" + fi + + cd -P "${EGIT_STORE_DIR}" \ + || die "${FUNCNAME}: can't chdir to \"${EGIT_STORE_DIR}\"" + # allow writing into EGIT_STORE_DIR + addwrite "${EGIT_STORE_DIR}" + # calculate the proper store dir for data + [[ -z ${EGIT_REPO_URI##*/} ]] && EGIT_REPO_URI="${EGIT_REPO_URI%/}" + if [[ -z ${EGIT_DIR} ]]; then + clone_dir=${EGIT_REPO_URI##*/} + EGIT_DIR=${EGIT_STORE_DIR}/${clone_dir} + fi + export EGIT_DIR=${EGIT_DIR} + debug-print "${FUNCNAME}: Storing the repo into \"${EGIT_DIR}\"." +} + +# @FUNCTION: git-2_move_source +# @DESCRIPTION: +# Internal function moving sources from the EGIT_DIR to EGIT_SOURCEDIR dir. +git-2_move_source() { + debug-print-function ${FUNCNAME} "$@" + + debug-print "${FUNCNAME}: ${MOVE_COMMAND} \"${EGIT_DIR}\" \"${EGIT_SOURCEDIR}\"" + pushd "${EGIT_DIR}" > /dev/null + mkdir -p "${EGIT_SOURCEDIR}" \ + || die "${FUNCNAME}: failed to create ${EGIT_SOURCEDIR}" + ${MOVE_COMMAND} "${EGIT_SOURCEDIR}" \ + || die "${FUNCNAME}: sync to \"${EGIT_SOURCEDIR}\" failed" + popd > /dev/null +} + +# @FUNCTION: git-2_initial_clone +# @DESCRIPTION: +# Internal function running initial clone on specified repo_uri. +git-2_initial_clone() { + debug-print-function ${FUNCNAME} "$@" + + local repo_uri + + EGIT_REPO_URI_SELECTED="" + for repo_uri in ${EGIT_REPO_URI}; do + debug-print "${FUNCNAME}: git clone ${EGIT_OPTIONS} \"${repo_uri}\" \"${EGIT_DIR}\"" + git clone ${EGIT_OPTIONS} "${repo_uri}" "${EGIT_DIR}" + if [[ $? -eq 0 ]]; then + # global variable containing the repo_name we will be using + debug-print "${FUNCNAME}: EGIT_REPO_URI_SELECTED=\"${repo_uri}\"" + EGIT_REPO_URI_SELECTED="${repo_uri}" + break + fi + done + + if [[ -z ${EGIT_REPO_URI_SELECTED} ]]; then + die "${FUNCNAME}: can't fetch from ${EGIT_REPO_URI}" + fi +} + +# @FUNCTION: git-2_update_repo +# @DESCRIPTION: +# Internal function running update command on specified repo_uri. +git-2_update_repo() { + debug-print-function ${FUNCNAME} "$@" + + local repo_uri + + if [[ -n ${EGIT_NONBARE} ]]; then + # checkout master branch and drop all other local branches + git checkout ${EGIT_MASTER} || die "${FUNCNAME}: can't checkout master branch ${EGIT_MASTER}" + for x in $(git branch | grep -v "* ${EGIT_MASTER}" | tr '\n' ' '); do + debug-print "${FUNCNAME}: git branch -D ${x}" + git branch -D ${x} > /dev/null + done + fi + + EGIT_REPO_URI_SELECTED="" + for repo_uri in ${EGIT_REPO_URI}; do + # git urls might change, so reset it + git config remote.origin.url "${repo_uri}" + + debug-print "${EGIT_UPDATE_CMD} ${EGIT_OPTIONS}" + ${EGIT_UPDATE_CMD} > /dev/null + if [[ $? -eq 0 ]]; then + # global variable containing the repo_name we will be using + debug-print "${FUNCNAME}: EGIT_REPO_URI_SELECTED=\"${repo_uri}\"" + EGIT_REPO_URI_SELECTED="${repo_uri}" + break + fi + done + + if [[ -z ${EGIT_REPO_URI_SELECTED} ]]; then + die "${FUNCNAME}: can't update from ${EGIT_REPO_URI}" + fi +} + +# @FUNCTION: git-2_fetch +# @DESCRIPTION: +# Internal function fetching repository from EGIT_REPO_URI and storing it in +# specified EGIT_STORE_DIR. +git-2_fetch() { + debug-print-function ${FUNCNAME} "$@" + + local oldsha cursha repo_type + + [[ -n ${EGIT_NONBARE} ]] && repo_type="non-bare repository" || repo_type="bare repository" + + if [[ ! -d ${EGIT_DIR} ]]; then + git-2_initial_clone + pushd "${EGIT_DIR}" > /dev/null + cursha=$(git rev-parse ${UPSTREAM_BRANCH}) + echo "GIT NEW clone -->" + echo " repository: ${EGIT_REPO_URI_SELECTED}" + echo " at the commit: ${cursha}" + + popd > /dev/null + elif [[ -n ${EVCS_OFFLINE} ]]; then + pushd "${EGIT_DIR}" > /dev/null + cursha=$(git rev-parse ${UPSTREAM_BRANCH}) + echo "GIT offline update -->" + echo " repository: $(git config remote.origin.url)" + echo " at the commit: ${cursha}" + popd > /dev/null + else + pushd "${EGIT_DIR}" > /dev/null + oldsha=$(git rev-parse ${UPSTREAM_BRANCH}) + git-2_update_repo + cursha=$(git rev-parse ${UPSTREAM_BRANCH}) + + # fetch updates + echo "GIT update -->" + echo " repository: ${EGIT_REPO_URI_SELECTED}" + # write out message based on the revisions + if [[ "${oldsha1}" != "${cursha1}" ]]; then + echo " updating from commit: ${oldsha}" + echo " to commit: ${cursha}" + else + echo " at the commit: ${cursha}" + fi + + # print nice statistic of what was changed + git --no-pager diff --stat ${oldsha}..${UPSTREAM_BRANCH} + popd > /dev/null + fi + # export the version the repository is at + export EGIT_VERSION="${cursha1}" + # log the repo state + [[ ${EGIT_COMMIT} != ${EGIT_BRANCH} ]] \ + && echo " commit: ${EGIT_COMMIT}" + echo " branch: ${EGIT_BRANCH}" + echo " storage directory: \"${EGIT_DIR}\"" + echo " checkout type: ${repo_type}" +} + +# @FUNCTION: git_bootstrap +# @DESCRIPTION: +# Internal function that runs bootstrap command on unpacked source. +git-2_bootstrap() { + debug-print-function ${FUNCNAME} "$@" + + # @ECLASS_VARIABLE: EGIT_BOOTSTRAP + # @DESCRIPTION: + # Command to be executed after checkout and clone of the specified + # repository. + # enviroment the package will fail if there is no update, thus in + # combination with --keep-going it would lead in not-updating + # pakcages that are up-to-date. + if [[ -n ${EGIT_BOOTSTRAP} ]]; then + pushd "${EGIT_SOURCEDIR}" > /dev/null + einfo "Starting bootstrap" + + if [[ -f ${EGIT_BOOTSTRAP} ]]; then + # we have file in the repo which we should execute + debug-print "${FUNCNAME}: bootstraping with file \"${EGIT_BOOTSTRAP}\"" + + if [[ -x ${EGIT_BOOTSTRAP} ]]; then + eval "./${EGIT_BOOTSTRAP}" \ + || die "${FUNCNAME}: bootstrap script failed" + else + eerror "\"${EGIT_BOOTSTRAP}\" is not executable." + eerror "Report upstream, or bug ebuild maintainer to remove bootstrap command." + die "\"${EGIT_BOOTSTRAP}\" is not executable" + fi + else + # we execute some system command + debug-print "${FUNCNAME}: bootstraping with commands \"${EGIT_BOOTSTRAP}\"" + + eval "${EGIT_BOOTSTRAP}" \ + || die "${FUNCNAME}: bootstrap commands failed" + fi + + einfo "Bootstrap finished" + popd > /dev/null + fi +} + +# @FUNCTION: git-2_migrate_repository +# @DESCRIPTION: +# Internal function migrating between bare and normal checkout repository. +# This is based on usage of EGIT_SUBMODULES, at least until they +# start to work with bare checkouts sanely. +git-2_migrate_repository() { + debug-print-function ${FUNCNAME} "$@" + + local target returnstate + + # first find out if we have submodules + if [[ -z ${EGIT_HAS_SUBMODULES} ]]; then + target="bare" + else + target="full" + fi + [[ -n ${EGIT_NONBARE} ]] && target="full" + + # test if we already have some repo and if so find out if we have + # to migrate the data + if [[ -d ${EGIT_DIR} ]]; then + if [[ ${target} == bare && -d ${EGIT_DIR}/.git ]]; then + debug-print "${FUNCNAME}: converting \"${EGIT_DIR}\" to bare copy" + + ebegin "Converting \"${EGIT_DIR}\" from non-bare to bare copy" + mv "${EGIT_DIR}/.git" "${EGIT_DIR}.bare" + export GIT_DIR="${EGIT_DIR}.bare" + git config core.bare true > /dev/null + returnstate=$? + unset GIT_DIR + rm -rf "${EGIT_DIR}" + mv "${EGIT_DIR}.bare" "${EGIT_DIR}" + eend ${returnstate} + fi + if [[ ${target} == full && ! -d ${EGIT_DIR}/.git ]]; then + debug-print "${FUNCNAME}: converting \"${EGIT_DIR}\" to non-bare copy" + + ebegin "Converting \"${EGIT_DIR}\" from bare to non-bare copy" + git clone -l "${EGIT_DIR}" "${EGIT_DIR}.nonbare" > /dev/null + returnstate=$? + rm -rf "${EGIT_DIR}" + mv "${EGIT_DIR}.nonbare" "${EGIT_DIR}" + eend ${returnstate} + fi + fi + if [[ ${returnstate} -ne 0 ]]; then + debug-print "${FUNCNAME}: converting \"${EGIT_DIR}\" failed, removing to start from scratch" + + # migration failed, remove the EGIT_DIR to play it safe + einfo "Migration failed, removing \"${EGIT_DIR}\" to start from scratch." + rm -rf "${EGIT_DIR}" + fi + + # set various options to work with both targets + if [[ ${target} == bare ]]; then + debug-print "${FUNCNAME}: working in bare repository for \"${EGIT_DIR}\"" + EGIT_OPTIONS+=" --bare" + MOVE_COMMAND="git clone -l -s -n ${EGIT_DIR// /\\ }" + EGIT_UPDATE_CMD="git fetch -f -u origin ${EGIT_BRANCH}:${EGIT_BRANCH}" + UPSTREAM_BRANCH="${EGIT_BRANCH}" + else + debug-print "${FUNCNAME}: working in bare repository for non-bare \"${EGIT_DIR}\"" + MOVE_COMMAND="cp -pPR ." + EGIT_UPDATE_CMD="git pull -f -u ${EGIT_OPTIONS}" + UPSTREAM_BRANCH="origin/${EGIT_BRANCH}" + EGIT_NONBARE="true" + fi +} + +# @FUNCTION: git-2_src_unpack +# @DESCRIPTION: +# Default git src_upack function. +git-2_src_unpack() { + debug-print-function ${FUNCNAME} "$@" + + git-2_init_variables + git-2_prepare_storedir + git-2_migrate_repository + git-2_fetch "$@" + git-2_gc + git-2_submodules + git-2_move_source + git-2_branch + git-2_bootstrap + echo ">>> Unpacked to ${EGIT_SOURCEDIR}" +} -- cgit v1.2.3-65-gdbad