#!/bin/bash
# This script creates a symlink named _lgtm_detected_source_root, pointing to
# the directory that most likely contains the build system for this project.

echo

# This function creates the symlink named _lgtm_detected_source_root,
# pointing to the first argument, and exits. Using this function helps to
# ensure that (1) the symlink gets the correct name and (2) we always exit
# after creating the symlink so later stages are not at risk of reading the
# symlink as part of their analysis.
function create_symlink_and_exit() {
  ln -s "$1" _lgtm_detected_source_root
  echo
  exit
}

# Returns success if the first argument is a directory that appears to contain
# files for a major build system.
function convincingly_has_build_system() {
  ( cd "$1" &&
    ( [[
        -x configure || \
        -f configure.in || -f configure.ac || \
        -f CMakeLists.txt || \
        -f meson.build || \
        -f wscript || \
        -f SConstruct ]] )
  )
}

# Returns success if the first argument is a directory that appears to contain
# files for some build system, possibly a home-made or minor one.
function may_have_build_system() {
  convincingly_has_build_system "$1" || \
  ( cd "$1" &&
    ( [[
        ( -f Kbuild && -f Kconfig ) || \
        -f Makefile || -f makefile || -f GNUmakefile || \
        "$(echo ./*.pro)" != "./*.pro" || \
        ( -x build && -f build ) || \
        -x build.sh || \
        -f setup.py ]] )
  )
}

# Holds if the first argument is a directory that does not appear to contain
# third-party code.
function is_allowed_dir() {
  ! [ -L "$1" ] && \
    [ -d "$1" ] && \
    echo "$1" | grep -vq '/\(thirdparty\|third_party\|third-party\|vendor\|external\|3rdparty\|_vendor\)$'
}

# Takes a list of file names as argument. If that list has length 1, and that
# one argument is a directory for which `is_allowed_dir` returns success, print
# that directory and return success. Otherwise, return failure.
function single_dir() {
  if [ $# -eq 1 ] && is_allowed_dir "$1"; then
    echo "$1"
  else
    false
  fi
}

# Takes a directory name as argument traverses down its subdirectories for as
# long as each directory is _trivial_, stopping when it reaches a non-trivial
# directory and printing that. A _trivial_ directory is one containing nothing
# but a single subdirectory (and possibly dot-files).
# This function will not traverse into a directory that seems to contain
# third-party code.
function first_nontrivial_dir() {
  curdir="$1"
  while the_single_dir="$(single_dir "$curdir"/*)"; do
    curdir="$the_single_dir"
  done
  echo "$curdir"
}

# First, find the top directory, which is usually the root of the repository.

top_candidate="$(first_nontrivial_dir .)"

if may_have_build_system "$top_candidate"; then
  echo "detect_source_root: Using build system found in '$top_candidate'"
  create_symlink_and_exit "$top_candidate"
fi

# If we are certain that the top directory cannot possibly contain a build
# system, check each subdirectory to see if there is a single one with a build
# system. We try to be careful not to confuse some random directory with a
# Makefile for the main source directory, so we call
# `convincingly_has_build_system` instead of `may_have_build_system`.

preferred_subdir=""
for below_top_candidate in "$top_candidate"/*; do
  is_allowed_dir "$below_top_candidate" || continue;
  subdir="$(first_nontrivial_dir "$below_top_candidate")"
  if convincingly_has_build_system "$subdir"; then
    if [ -n "$preferred_subdir" ]; then
      echo "detect_source_root: At least these two subdirs have build system files in them:"
      echo "  $preferred_subdir"
      echo "  $subdir"
      echo "To avoid ambiguity, build will be attempted from '$top_candidate'"
      echo "even though no build system was found there."
      create_symlink_and_exit "$top_candidate"
    else
      preferred_subdir="$subdir"
    fi
  fi
done

if [ -n "$preferred_subdir" ]; then
  echo "detect_source_root: Build will be attempted from '$preferred_subdir'"
  create_symlink_and_exit "$preferred_subdir"
else
  echo "detect_source_root: Build will be attempted from '$top_candidate'"
  echo "even though no build system was found there."
  create_symlink_and_exit "$top_candidate"
fi
