248 lines
6.8 KiB
Bash
248 lines
6.8 KiB
Bash
|
#!/bin/bash
|
||
|
# This is a helper for rpm which collects 'Provides' and 'Requires' information from OCaml files.
|
||
|
# It reads a list of filenames from STDIN.
|
||
|
# It expects as argument either '--provides|-P' or '--requires|-R'.
|
||
|
# Additional optional arguments are:
|
||
|
# -f "ocamlobjinfo command"
|
||
|
# -c # ignored, recognized just for compat reasons
|
||
|
# -i NAME # omit the Requires/Provides for this bytecode unit name
|
||
|
# -x NAME # omit the Requires/Provides for this native unit name
|
||
|
#
|
||
|
# OCaml object files contain either bytecode or native code.
|
||
|
# Each bytecode variant provides a certain interface, which is represented by a hash.
|
||
|
# Each native variant provides a certain interface and a certain implementation, which are represented by hashes.
|
||
|
# Each variant may also require a certain interface and/or implementation provided by other files.
|
||
|
# The details for each file can be inspected with 'ocamlobjinfo'.
|
||
|
#
|
||
|
# Each file contains at least one module.
|
||
|
# Information about each module follows after a line starting with "Name:" or "Unit name:":
|
||
|
#
|
||
|
# cma/cmi/cmo (bytecode):
|
||
|
# Unit name: NAME
|
||
|
# Interfaces imported:
|
||
|
# HASH NAME
|
||
|
# HASH NAME_FROM_OTHER_MODULE
|
||
|
#
|
||
|
# cmx/cmxa/cmxs (native):
|
||
|
# Name: NAME
|
||
|
# CRC of implementation: HASH
|
||
|
# Interfaces imported:
|
||
|
# HASH NAME
|
||
|
# HASH NAME_FROM_OTHER_MODULE
|
||
|
# Implementations imported:
|
||
|
# HASH NAME_FROM_OTHER_MODULE
|
||
|
#
|
||
|
# cmxs files are recoqnized, but need to be ignored.
|
||
|
# They contain references of the interfaces and implementations
|
||
|
# compiled into them.
|
||
|
#
|
||
|
# The hash may contain just '-', in which case it is ignored.
|
||
|
#
|
||
|
# Output:
|
||
|
# ocaml(NAME) = HASH # for interfaces (bytecode and native)
|
||
|
# ocamlx(NAME) = HASH # for implementations (native)
|
||
|
|
||
|
set -e
|
||
|
#
|
||
|
OCAMLOBJINFO=ocamlobjinfo
|
||
|
rpm_prefix_interface='ocaml'
|
||
|
rpm_prefix_implementation='ocamlx'
|
||
|
#
|
||
|
parse() {
|
||
|
local filename="$1"
|
||
|
|
||
|
${OCAMLOBJINFO} "${filename}" | awk '
|
||
|
BEGIN {
|
||
|
debug=0
|
||
|
mode=ENVIRON["mode"]
|
||
|
RPM_BUILD_ROOT=ENVIRON["RPM_BUILD_ROOT"]
|
||
|
rpm_prefix_interface=ENVIRON["rpm_prefix_interface"]
|
||
|
rpm_prefix_implementation=ENVIRON["rpm_prefix_implementation"]
|
||
|
state="find"
|
||
|
unit=""
|
||
|
|
||
|
split(ENVIRON["ignore_implementation"], ignore_implementation_a)
|
||
|
for (i in ignore_implementation_a) {
|
||
|
val=ignore_implementation_a[i]
|
||
|
if (debug)
|
||
|
printf "INFO: ignore_implementation %s\n", val > "/dev/stderr"
|
||
|
ignore_implementation[val]=1
|
||
|
}
|
||
|
split(ENVIRON["ignore_interface"], ignore_interface_a)
|
||
|
for (i in ignore_interface_a) {
|
||
|
val=ignore_interface_a[i]
|
||
|
if (debug)
|
||
|
printf "INFO: ignore_interface %s\n", val > "/dev/stderr"
|
||
|
ignore_interface[val]=1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/^File / {
|
||
|
if (RPM_BUILD_ROOT != "" ) {
|
||
|
file=substr($2,length(RPM_BUILD_ROOT)+1)
|
||
|
} else {
|
||
|
file=$2
|
||
|
}
|
||
|
state="file"
|
||
|
next
|
||
|
}
|
||
|
/^Unit name:/ {
|
||
|
unit=$3
|
||
|
state="cma"
|
||
|
next
|
||
|
}
|
||
|
/^Name:/ {
|
||
|
unit=$2
|
||
|
state="cmx"
|
||
|
next
|
||
|
}
|
||
|
|
||
|
/^CRC of implementation:/ {
|
||
|
if (state == "cmx") {
|
||
|
if (ignore_implementation[unit] != "") {
|
||
|
if (ignore_implementation[unit] != "seen") {
|
||
|
printf "INFO: ignoring Provides %s(%s)=%s from %s\n", rpm_prefix_implementation, unit, $4, file > "/dev/stderr"
|
||
|
ignore_implementation[unit]="seen"
|
||
|
}
|
||
|
} else {
|
||
|
implementation_provides[unit]=$4
|
||
|
}
|
||
|
} else {
|
||
|
printf "WARN: state %s, expected cmx, got %s\n", state, $0 > "/dev/stderr"
|
||
|
}
|
||
|
state="crc"
|
||
|
next
|
||
|
}
|
||
|
|
||
|
/^Interfaces imported:/ {
|
||
|
state="interface"
|
||
|
next
|
||
|
}
|
||
|
|
||
|
/^Implementations imported:/ {
|
||
|
state="implementation"
|
||
|
next
|
||
|
}
|
||
|
|
||
|
/^\t/ {
|
||
|
if (state == "interface" && NF > 1 && match($1, "^-") == 0) {
|
||
|
if (unit == $2) {
|
||
|
if (ignore_interface[unit] != "") {
|
||
|
if (ignore_interface[unit] != "seen") {
|
||
|
printf "INFO: ignoring Provides %s(%s)=%s from %s\n", rpm_prefix_interface, unit, $1, file > "/dev/stderr"
|
||
|
ignore_interface[unit]="seen"
|
||
|
}
|
||
|
} else {
|
||
|
interface_provides[unit]=$1
|
||
|
}
|
||
|
} else {
|
||
|
if (ignore_interface[$2] != "") {
|
||
|
if (ignore_interface[$2] != "seen") {
|
||
|
printf "INFO: ignoring Requires %s(%s)=%s from %s\n", rpm_prefix_interface, $2, $1, file > "/dev/stderr"
|
||
|
ignore_interface[$2]="seen"
|
||
|
}
|
||
|
} else {
|
||
|
interface_requires[$2]=$1
|
||
|
}
|
||
|
}
|
||
|
next
|
||
|
} else if (state == "implementation" && NF > 1 && match($1, "^-") == 0) {
|
||
|
if (unit == $2) {
|
||
|
if (ignore_implementation[unit] != "") {
|
||
|
if (ignore_implementation[unit] != "seen") {
|
||
|
printf "INFO: ignoring Provides %s(%s)=%s from %s\n", rpm_prefix_implementation, unit, $1, file > "/dev/stderr"
|
||
|
ignore_implementation[unit]="seen"
|
||
|
}
|
||
|
} else {
|
||
|
implementation_provides[unit]=$1
|
||
|
}
|
||
|
} else {
|
||
|
if (ignore_implementation[$2] != "") {
|
||
|
if (ignore_implementation[$2] != "seen") {
|
||
|
printf "INFO: ignoring Requires %s(%s)=%s from %s\n", rpm_prefix_implementation, $2, $1, file > "/dev/stderr"
|
||
|
ignore_implementation[$2]="seen"
|
||
|
}
|
||
|
} else {
|
||
|
implementation_requires[$2]=$1
|
||
|
}
|
||
|
}
|
||
|
next
|
||
|
} else {
|
||
|
next
|
||
|
}
|
||
|
}
|
||
|
/^.*/ {
|
||
|
state="find"
|
||
|
}
|
||
|
|
||
|
END {
|
||
|
if (mode == "provides") {
|
||
|
for (i in interface_provides) {
|
||
|
printf "%s(%s) = %s\n", rpm_prefix_interface, i, interface_provides[i]
|
||
|
}
|
||
|
for (i in implementation_provides) {
|
||
|
printf "%s(%s) = %s\n", rpm_prefix_implementation, i, implementation_provides[i]
|
||
|
}
|
||
|
}
|
||
|
if (mode == "requires") {
|
||
|
for (i in interface_requires) {
|
||
|
printf "%s(%s) = %s\n", rpm_prefix_interface, i, interface_requires[i]
|
||
|
}
|
||
|
for (i in implementation_requires) {
|
||
|
printf "%s(%s) = %s\n", rpm_prefix_implementation, i, implementation_requires[i]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
'
|
||
|
}
|
||
|
#
|
||
|
#
|
||
|
usage() {
|
||
|
echo >&2 "Usage: ${0##*/} -provides|-requires [-f 'ocamlobjinfo cmd']"
|
||
|
}
|
||
|
#
|
||
|
mode=
|
||
|
ignore_implementation_a=()
|
||
|
ignore_interface_a=()
|
||
|
while test "$#" -gt 0
|
||
|
do
|
||
|
: "${1}" "${2}"
|
||
|
case "${1}" in
|
||
|
-P|--provides) mode='provides' ;;
|
||
|
-R|--requires) mode='requires' ;;
|
||
|
-i) ignore_interface_a+=("$2") ; shift ;;
|
||
|
-x) ignore_implementation_a+=("$2") ; shift ;;
|
||
|
-f) OCAMLOBJINFO="$2"; shift ;;
|
||
|
-h|--help) usage ; exit 0 ;;
|
||
|
-c) ;; # ignored
|
||
|
--) break ;;
|
||
|
*) usage ; exit 1 ;;
|
||
|
esac
|
||
|
shift
|
||
|
done
|
||
|
if test -z "${mode}"
|
||
|
then
|
||
|
usage
|
||
|
exit 1
|
||
|
fi
|
||
|
#
|
||
|
export rpm_prefix_interface
|
||
|
export rpm_prefix_implementation
|
||
|
export mode
|
||
|
export ignore_implementation="${ignore_implementation_a[@]}"
|
||
|
export ignore_interface="${ignore_interface_a[@]}"
|
||
|
#
|
||
|
while read filename
|
||
|
do
|
||
|
case "${filename}" in
|
||
|
*.cma) parse "${filename}" ;;
|
||
|
*.cmi) parse "${filename}" ;;
|
||
|
*.cmo) parse "${filename}" ;;
|
||
|
*.cmx) parse "${filename}" ;;
|
||
|
*.cmxa) parse "${filename}" ;;
|
||
|
*.cmxs) ;;
|
||
|
*) continue ;;
|
||
|
esac
|
||
|
done
|
||
|
# vim: tw=666 ts=2 shiftwidth=2 et
|