From 753b60b0f479e226ecdf7e781804906aa8ee369f Mon Sep 17 00:00:00 2001 From: william Date: Wed, 22 Apr 2015 15:50:23 -0700 Subject: update luapath script --- mk/lua.path | 459 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 402 insertions(+), 57 deletions(-) diff --git a/mk/lua.path b/mk/lua.path index eb10d39..8df41c9 100755 --- a/mk/lua.path +++ b/mk/lua.path @@ -22,7 +22,7 @@ # warned. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# +# # Changelog: # # * 2013-08-02 - Published. Derived from an earlier script, lua.path, @@ -50,9 +50,41 @@ # # * 2013-10-22 - Initial ldflags detection. # +# * 2014-01-26 - Migrate CC vendor detection from external script. +# +# * 2014-09-29 - Add ldir and cdir modes which print install path by parsing +# package.path and package.cpath. +# +# * 2014-12-18 - Add -e GLOB option. +# +# Deprecate ldir and cdir modes. +# +# Add package.path and package.cpath to replace ldir and dir modes. +# Optional arguments to the new modes are preferred install paths, +# rather than globs for finding the lua utility path (use the new -e +# option, instead). +# +# * 2014-12-19 - Fix pkg-config version matching. The --modversion of +# the lua package might be stale. For example, it's 5.2.0 on Ubuntu +# 14.04 even though the Lua release is 5.2.3. +# +# Use the interpreter path as a reference point when searching for +# headers. $(dirname ${LUA_PATH})/../include is a very likely location +# as bindir and includedir have the same prefix in most installations. +# +# * 2015-01-15 - Quote more command names and arguments. Still need to +# handle space characters in code that employs command substitution. I +# think we could handle all whitespace characters, including newlines, +# by using a control character in IFS and using --exec printf "%s\1" {} +# rather than -print with find(1). +# +# * 2015-01-19 - Add fix for LuaJIT's default package.cpath, which tends to +# hardcode /usr/local/lib/lua/5.1, ordered before the LuaJIT +# installation prefix. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Copyright (C) 2012-2013 William Ahern +# Copyright (C) 2012-2015 William Ahern # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -81,8 +113,10 @@ export LC_ALL=C # no locale headaches unset IFS # no field splitting surprises : ${TMPDIR:=/tmp} # sane TMPDIR : ${CC:=cc} +unset LUA_PATH || true # interferes search for module install directory +unset LUA_CPATH || true -MYVERSION=20131025 +MYVERSION=20150119 MYVENDOR="william@25thandClement.com" @@ -99,6 +133,10 @@ MAXDEPTH= # full command switch, like "-maxdepth 3", if supported XDEV= # do not cross device boundaries; i.e. "-xdev" SHORTEST= # continue searching until shortest pathname found PKGCONFIG= # path to pkg-config, found by `command -v` when -k option invoked +GLOB= # -e GLOB expression for lua, luac, ldir, and cdir + +GLOB_LUA="lua:lua[5-9]*:lua-[5-9]*:luajit*" +GLOB_LUAC="luac:luac[5-9]*:luac-[5-9]*" API_MIN=500 API_MAX=999 @@ -135,7 +173,7 @@ xdirs() { set -- ${2:-} - while [ $# -gt 0 ]; do + while [ $# -gt 0 ]; do case "${1}" in -${OPTC}) shift @@ -167,27 +205,42 @@ ldirs() { xdirs "L" "${1:-}" } -# count ":" delimited directories generated by xdirs -ndirs() { +# count ":"-delimited substrings +count() { IFS=: set -- ${1:-} unset IFS - printf "$#\n" + printf "$#" } +# append to ":"-delimited string variable +append() { + NAME=${1} + eval VALUE="\${${NAME}}" + shift + + IFS=: + TMP="$*" + + IFS="\n" + read -r "${NAME}" <<-EOF + ${VALUE:-}${VALUE:+:}${TMP} + EOF + unset IFS +} # # evalmacro PATH MACRO [REGEX] [SUBST] # # PATH Header identifier--#include # MACRO Macro identifier -# REGEX Optional regex pattern to match macro evalutation result +# REGEX Optional regex pattern to match macro evaluation result # SUBST Optional replacement expression # evalmacro() { printf "#include <$1>\n[===[$2]===]\n" \ - | ${CC:-cc} ${CPPFLAGS:-} -E - 2>>/dev/null \ + | "${CC:-cc}" ${CPPFLAGS:-} -E - 2>>/dev/null \ | sed -ne " s/^.*\\[===\\[ *\\(${3:-.*}\\) *\\]===\\].*$/${4:-\\1}/ t Found @@ -219,7 +272,7 @@ testsym() { tryluainclude() { - V="$(evalmacro ${1} LUA_VERSION_NUM '[0123456789][0123456789]*')" + V="$(evalmacro "${1}" LUA_VERSION_NUM '[0123456789][0123456789]*')" : ${V:=0} if [ "${1%/*}" != "${1}" ]; then @@ -242,7 +295,7 @@ tryluainclude() { [ "$V" -ge "${API_MIN}" -a "$V" -le "${API_MAX}" ] || return 0 if [ -n "${JIT_REQ}" ]; then - J="$(evalmacro ${1%%lua.h}luajit.h LUAJIT_VERSION_NUM '[0123456789][0123456789]*')" + J="$(evalmacro "${1%%lua.h}luajit.h" LUAJIT_VERSION_NUM '[0123456789][0123456789]*')" : ${J:=0} if [ "${JIT_REQ}" = "skip" ]; then @@ -285,6 +338,167 @@ foundversion() { } +# +# luapc +# +# wrapper around `pkg-config ... LIB`, where LIB is derived by +# searching for all libraries with "lua" in the name that have a +# --modversion equal to the release version printed by ${LUA_PATH} -v. +# +LUAPC_LIB= + +luapc() { + [ -n "${LUA_PATH}" ] || return 0 + + [ -n "${PKGCONFIG}" ] || return 0 + + # find pkg-config library name + if [ -z "${LUAPC_LIB}" ]; then + V="$("${LUA_PATH}" -v &1 | head -n1 | sed -ne 's/^Lua[^ ]* \([0123456789][0123456789]*\(\.[0123456789][0123456789]*\)*\).*/\1/p')" + + [ -n "${V}" ] || return 0 + + V_N=$(mmp2num "${V}") + + for LIB in $("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p'); do + M="$("${PKGCONFIG}" --modversion ${LIB} || true)" + + # break immediately on exact match + if [ "${V}" = "${M}" ]; then + LUAPC_LIB="${LIB}" + + break + fi + + # + # NOTE: On Ubuntu 14.04 pkg-config --modversion + # lua5.2 prints 5.2.0 even though the release + # version is 5.2.3 (what lua5.2 -v prints). + # + # If the major.minor components match, then + # tentatively use that package name. + # + M_N=$(mmp2num "${M}" 0 0 0) + + if [ "$((${V_N} / 100))" -eq "$((${M_N} / 100))" ]; then + LUAPC_LIB="${LIB}" + fi + done + + [ -n "${LUAPC_LIB}" ] || return 0 + fi + + ${PKGCONFIG} "$@" "${LUAPC_LIB}" >/dev/null || true +} + + +# +# findinstalldir package.path|package.cpath [preferred-path ...] +# +findinstalldir() { + V_DIR=$((${LUA_VER} / 100 % 100)).$((${LUA_VER} % 100)) + + if [ "${1}" = "package.cpath" ]; then + DIR="$(luapc --variable INSTALL_CMOD)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}" + + DIR="$(luapc --variable INSTALL_LIB)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}" + + DIR="$(luapc --variable libdir)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}" + + DIR="$(luapc --variable prefix)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}/lib/lua/${V_DIR}" + + # LuaJIT installations tend to include + # /usr/local/lib/lua/5.1 as one of the first paths, ordered + # before the LuaJIT installation prefix, and regardless of + # whether there exists a /usr/local/lib/lua/5.1. + set -- "$@" "${LUA_PATH}/../../lib/lua/${V_DIR}" + set -- "$@" "${LUA_PATH}/../../lib/*/lua/${V_DIR}" # e.g. lib/x86_64-linux-gnu + else + DIR="$(luapc --variable INSTALL_LMOD)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}" + + DIR="$(luapc --variable prefix)" + [ -n "${DIR}" ] && set -- "$@" "${DIR}/share/lua/${V_DIR}" + + # See above LuaJIT note. Although the built-in package.path + # usually orders the LuaJIT installation prefix first. + set -- "$@" "${LUA_PATH}/../../share/lua/${V_DIR}" + fi + + ARRAY="${1}" + shift + + if [ $# -eq 0 ]; then + set -- "/nonexistent" # cannot expand empty $@ on some implementations + fi + + "${LUA_PATH}" - "$@" <<-EOF + -- + -- actual pkg-config variable on Ubuntu 14.04 + -- + -- /usr//share/lua/5.1 + -- + local function fixpath(path) + local stack = { path:match"^/" and "" or "." } + + for ent in path:gmatch"([^/]+)" do + if ent == ".." and #stack > 1 then + stack[#stack] = nil + elseif ent ~= "." then + stack[#stack + 1] = ent + end + end + + return table.concat(stack, "/") + end + + local function topattern(path) + if string.match(path, "*") then + path = string.gsub(path, "%%", "%%") + return string.gsub(path, "*", "[^/]+") + end + end + + local dirs = { } + + for dir in ${ARRAY}:gmatch"([^;?]+)/" do + dir = fixpath(dir) + + if dir ~= "." then + dirs[#dirs + 1] = dir + end + end + + for _, arg in ipairs{ ... } do + arg = fixpath(arg) + + local pat = topattern(arg) + + for _, dir in ipairs(dirs) do + if arg == dir then + print(dir) + os.exit(0) + elseif pat and string.match(dir, pat) then + print(dir) + os.exit(0) + end + end + end + + if dirs[1] then + print(dirs[1]) + os.exit(0) + else + os.exit(1) + end + EOF +} + + # # findversion # @@ -315,7 +529,7 @@ findversion() { if [ -n "${PKGCONFIG}" ]; then - PKGFLAGS="$(${PKGCONFIG} --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --cflags 2>>/dev/null | cat)" + PKGFLAGS="$("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --cflags 2>>/dev/null | cat)" PKGDIRS="$(idirs "${PKGFLAGS}")" IFS=: @@ -349,7 +563,10 @@ findversion() { fi - [ "${RECURSE}" = "yes" ] || return 0 + if [ "${RECURSE}" != "yes" ]; then + [ "${API_VER:-0}" -gt 0 ] + return $? + fi # recurse into CPPDIRS @@ -359,7 +576,7 @@ findversion() { if [ $# -gt 0 ]; then for D; do - for F in $(find ${D} ${MAXDEPTH} ${XDEV} -name lua.h -print 2>>/dev/null); do + for F in $(find "${D}" ${MAXDEPTH} ${XDEV} -name lua.h -print 2>>/dev/null); do tryluainclude "${F}" if foundversion; then @@ -377,7 +594,7 @@ findversion() { if [ $# -gt 0 ]; then for D; do - for F in $(find ${D}/. ${MAXDEPTH} ${XDEV} -name lua.h -print 2>>/dev/null); do + for F in $(find "${D}/." ${MAXDEPTH} ${XDEV} -name lua.h -print 2>>/dev/null); do tryluainclude "${F}" if foundversion; then @@ -386,6 +603,26 @@ findversion() { done done fi + + + # if we can find the lua interpreter, use it as a reference for + # header locations. + if findlua; then + D="${LUA_PATH%/*}" + D="${D%/*}/include" + + if [ -d "${D}" ]; then + for F in $(find "${D}" ${MAXDEPTH} ${XDEV} -name lua.h -print 2>>/dev/null); do + tryluainclude "${F}" + + if foundversion; then + return 0 + fi + done + fi + fi + + [ "${API_VER:-0}" -gt 0 ] } @@ -487,12 +724,12 @@ foundlib() { findlib() { if [ -n "${PKGCONFIG}" ]; then - PKGFLAGS="$(${PKGCONFIG} --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --libs 2>>/dev/null | cat)" + PKGFLAGS="$("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --libs 2>>/dev/null | cat)" PKGDIRS="$(ldirs "${PKGFLAGS}")" PKGDIRS="${PKGDIRS}${PKGDIRS:+:}/lib:/usr/lib:/usr/local/lib" - NUMDIRS="$(ndirs "${PKGDIRS}")" + NUMDIRS="$(count "${PKGDIRS}")" PKGLIBS="$(xdirs "l" "${PKGFLAGS}")" - NUMLIBS="$(ndirs "${PKGLIBS}")" + NUMLIBS="$(count "${PKGLIBS}")" ALLDIRS="${PKGDIRS}${PKGLIBS:+:}${PKGLIBS}" IFS=: @@ -575,11 +812,17 @@ safeperm() { findluac() { + if [ $# -eq 0 ]; then + IFS=: + set -- ${GLOB:-${GLOB_LUAC}} + unset IFS + fi + while [ $# -gt 0 ]; do for F in $(findpath "${1}" no "${PATH}"; findpath "${1}" "${RECURSE}" "${BINDIRS}"); do [ -x "$F" ] && safeperm "$F" || continue - V="$($F -v &1 | head -n1 | sed -ne 's/^Lua \([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" + V="$("$F" -v &1 | head -n1 | sed -ne 's/^Lua \([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" : ${V:=0} V="$((${V%%.*} * 100 + ${V##*.} % 100))" @@ -599,26 +842,46 @@ findluac() { shift done + + if [ -n "${LUAC_PATH}" -a -n "${LUAC_VER}" ]; then + return 0 + else + return 1 + fi +} + + +isinteger() { + I="${1}" + + [ "${#I}" -gt 0 ] || return 1 + + while [ "${#I}" -gt 0 ]; do + if [ "${I##[0123456789]}" = "${I}" ]; then + return 1 + fi + + I=${I##[0123456789]} + done + + return 0 } checkints() { while [ $# -gt 0 ]; do - I="${1}" - while [ "${#I}" -gt 0 ]; do - if [ "${I##[0123456789]}" = "${I}" ]; then - printf -- "${0##*/}: ${1}: not a number\n" >&2 - exit 1 - fi - - I=${I##[0123456789]} - done + if ! isinteger "${1}"; then + printf -- "${0##*/}: ${1}: not a number\n" >&2 + return 1 + fi shift done } +# Only major.minor for matching LUA_VERSION_NUM in lua.h. Also, _VERSION +# only includes major.minor. lua2num() { M=0 m="${2:-0}" @@ -636,6 +899,7 @@ lua2num() { } +# All major.minor.patch for matching LUAJIT_VERSION_NUM in luajit.h. jit2num() { M=0 m="${2:-0}" @@ -655,12 +919,45 @@ jit2num() { } +mmp2num() { + M="${2:-0}" + m="${3:-0}" + p="${4:-0}" + + IFS=".+-_" + set -- ${1} + unset IFS + + if isinteger "${1:-}"; then + M=${1} + fi + + if isinteger "${2:-}"; then + m=${2} + fi + + if isinteger "${3:-}"; then + p=${3} + fi + + checkints $M $m $p + + printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n" +} + + findlua() { + if [ $# -eq 0 ]; then + IFS=: + set -- ${GLOB:-${GLOB_LUA}} + unset IFS + fi + while [ $# -gt 0 ]; do for F in $(findpath "${1}" no "${PATH}"; findpath "${1}" "${RECURSE}" "${BINDIRS}"); do [ -x "$F" ] && safeperm "$F" || continue - V="$($F -e 'print(string.match(_VERSION, [[[%d.]+]]))' >/dev/null | head -n1 | sed -ne 's/^\([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" + V="$("$F" -e 'print(string.match(_VERSION, [[[%d.]+]]))' >/dev/null | head -n1 | sed -ne 's/^\([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" : ${V:=0} V="$((${V%%.*} * 100 + ${V##*.} % 100))" @@ -671,7 +968,7 @@ findlua() { [ "${V}" -ge "${API_MIN}" -a "${V}" -le "${API_MAX}" ] || continue if [ -n "${JIT_REQ}" ]; then - J="$($F -v &1 | head -n1 | sed -ne 's/^LuaJIT \([0123456789][0123456789]*\.[0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" + J="$("$F" -v &1 | head -n1 | sed -ne 's/^LuaJIT \([0123456789][0123456789]*\.[0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" J="$(jit2num ${J:-0})" if [ "${JIT_REQ}" = "skip" ]; then @@ -691,12 +988,33 @@ findlua() { shift done + + if [ -n "${LUA_PATH}" -a -n "${LUA_VER}" ]; then + return 0 + else + return 1 + fi +} + + +ccname() { + "${CC}" -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' + #if defined __SUNPRO_C + sunpro + #elif defined __clang__ + clang + #elif defined __GNUC__ + gcc + #else + other + #endif + EOF } usage() { cat <<-EOF - usage: ${0##*/} [-I:L:P:d:Dkrm:xsv:j:JVh] cppflags|ldflags|version|lua|luac + usage: ${0##*/} [-I:L:P:d:De:krm:xsv:j:JVh] cppflags|version|lua|luac|... -I PATH additional search directory for includes -L PATH additional search directory for libraries -P PATH additional search directory for binaries @@ -705,6 +1023,7 @@ usage() { unless a trailing "/" is present (default sandbox is \$TMPDIR/${0##*/}-XXXXXXXXXXXXXXXX) -D do not create a sandbox + -e GLOB glob pattern for finding utilities (lua, luac, etc) -k query pkg-config if available -r recursively search directories -m MAXDEPTH limit recursion to MAXDEPTH (only for GNU and BSD find) @@ -718,15 +1037,16 @@ usage() { -V print this script's version information -h print this usage message - cppflags print derived additional CPPFLAGS necessary - ldflags print derived additional LDFLAGS necessary (TODO) - version print derived Lua API version - luac [GLOB] print path to luac utility using optional glob patterns - (e.g. "luac5.?"; default is "luac*") - lua [GLOB] print path to lua interpreter using optional glob patterns - (e.g. "lua luajit"; default is "lua*") - evalmacro run internal macro evaluator for debugging - testsym run internal library symbol reader for debugging + cppflags print derived additional CPPFLAGS necessary + ldflags print derived additional LDFLAGS necessary (TODO) + version print derived Lua API version + luac print path to luac utility ($(printf "${GLOB_LUA}" | tr ':' ' ')) + lua print path to lua interpreter ($(printf "${GLOB_LUAC}" | tr ':' ' ')) + package.path print preferred module install path + package.cpath print preferred C module install path + ccname print CC name (e.g. sunpro, clang, gcc) + evalmacro run internal macro evaluator for debugging + testsym run internal library symbol reader for debugging This utility is used to derive compiler flags and filesystem paths necessary to utilize Lua, LuaJIT, and particular versions thereof. @@ -767,7 +1087,7 @@ version() { } -while getopts I:L:P:d:Dkrm:xsv:j:JVh OPT; do +while getopts I:L:P:d:De:krm:xsv:j:JVh OPT; do case "${OPT}" in I) INCDIRS="${INCDIRS:-}${INCDIRS:+:}${OPTARG}" @@ -784,6 +1104,9 @@ while getopts I:L:P:d:Dkrm:xsv:j:JVh OPT; do D) SANDBOX= ;; + e) + GLOB="${GLOB:-}${GLOB:+:}${OPTARG}" + ;; k) PKGCONFIG="$(command -v pkg-config || true)" ;; @@ -857,8 +1180,8 @@ done shift $(($OPTIND - 1)) -for U in ${CC:-cc} find grep od rm rmdir sed xargs; do - if ! command -v ${U} >>/dev/null 2>&1; then +for U in "${CC:-cc}" find grep od rm rmdir sed xargs; do + if ! command -v "${U}" >>/dev/null 2>&1; then printf -- "${0##*/}: ${U}: command not found\n" >&2 fi done @@ -911,7 +1234,7 @@ fi case "${1:-}" in cppflags) - findversion + findversion || exit 1 [ "${API_VER:-0}" -gt 0 ] || exit 1 @@ -927,9 +1250,7 @@ ldflags) ;; version) - findversion - - [ "${API_VER:-0}" -gt 0 ] || exit 1 + findversion || exit 1 printf "$(((${API_VER} / 100) % 100)).$((($API_VER) % 100))\n" @@ -945,30 +1266,54 @@ libv*) luac) shift - if [ $# -eq 0 ]; then - set -- luac\* + if [ $# -gt 0 ]; then + append GLOB $* fi - findluac $* + findluac || exit 1 - [ -n "${LUAC_PATH}" ] || exit 1 - printf -- "${LUAC_PATH}\n" ;; lua) shift - if [ $# -eq 0 ]; then - set -- lua\* + if [ $# -gt 0 ]; then + append GLOB $* fi - findlua $* + findlua || exit 1 - [ -n "${LUA_PATH}" ] || exit 1 - printf -- "${LUA_PATH}\n" + ;; +ldir|cdir) + printf -- "${0##*/}: ${1}: deprecated command\n" >&2 + MODE="${1}" + shift + + if [ $# -gt 0 ]; then + append GLOB $* + fi + + findlua || exit 1 + + if [ "${MODE}" = "cdir" ]; then + findinstalldir package.cpath + else + findinstalldir package.path + fi + + ;; +package.path|package.cpath) + findlua || exit 1 + + findinstalldir "$@" || exit 1 + + ;; +ccname) + ccname + ;; evalmacro) shift -- cgit v1.2.3-59-g8ed1b