build.cmds.CmdCreatePkgConfig: Add module

Implement the functionality of create-pkg-config.sh in a Python
module CmdCreatePkgConfig.py. This allows to remove
create-pkg-config.sh and jw-build-functions.sh.

Note that the translation was done pretty literally to play it safe.
More code can and should be removed by taking advantage of the fact
that jw-projects.py knows more about the project than the shell
scripts.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2025-11-16 17:31:16 +01:00
commit 31537a0bd6
4 changed files with 59 additions and 557 deletions

View file

@ -63,8 +63,8 @@ ifeq ($(CREATE_PKG_CONFIG),true)
CREATE_PKG_CONFIG_OPTS += -l "-L$(PROJECT_DIRPATH)/lib -l$(PROJECT)"
endif
$(LOCAL_PKG_CONFIG): $(PROJECT_DESCR_FILE) $(JWBDIR)/make/make.mk $(JWB_SCRIPT_DIR)/create-pkg-config.sh
/bin/bash $(JWB_SCRIPT_DIR)/create-pkg-config.sh \
$(LOCAL_PKG_CONFIG): $(PROJECT_DESCR_FILE) $(JWBDIR)/make/make.mk
$(proj_query_cmd) create-pkg-config \
-F $< \
-n $(PROJECT) \
-v $(DIST_VERSION) \

View file

@ -1,161 +0,0 @@
#!/bin/bash
log()
{
echo $@
}
err()
{
echo $@ >&2
}
fatal()
{
err "Fatal: $@"
exit 1
}
try_assign_sec()
{
local file="$1"
local sec="$2"
local val=`_jwbuild_cat_section "$file" "$sec"`
[ -n "$val" ] && eval $3=\"$val\"
}
cleanup_requires()
{
# echo $@ | sed -e '
# s/\([a-zA-Z0-9.-]\+\) *\([a-zA-Z0-9.-]\+\) */\1, \2 /g
# #s/\([a-zA-Z-]\+\) *\([<>=]*\) *\([a-zA-Z0-9\.-]*\)/\1 \2 \3,/g
# #s/,,/,/g
# #s/,$//
# '
echo $@ | sed -e '
s/^ //g
s/\([ ]\|$\)\+/, /g
s/, $//
s/, $//
s/ *,* *\([<>=]\+\) *,* */ \1 /g
'
}
usage()
{
cat <<- EOT | sed 's/^ *|//'
|
| $myname [options]
|
| options are
|
| -h: display this help message and exit sucessfully
| -F filename read project description file
| -d "description" use description
|
EOT
goodbye $1
}
project_descr_file="$1"
myname=`basename $0`
set -e
eval set -- `getopt -- hF:d:n:s:p:v:c:l:V:r:R: "$@"`
while [ "$1" != -- ]; do
case $1 in
-h)
usage 0;;
-F)
project_descr_file="$2"
shift
;;
-d)
description="$2"
shift
;;
-n)
name="$2"
shift
;;
-s)
summary="$2"
shift
;;
-p)
prefix="$2"
shift
;;
-v)
version="$2"
shift
;;
-c)
cflags="$2"
shift
;;
-l)
libflags="$2"
shift
;;
-r)
requires_run="$2"
shift
;;
-R)
requires_build="$2"
shift
;;
-V)
variables="$variables$2\n"
shift
;;
*)
usage 1;;
esac
shift
done
shift
set +e
[ -r "$project_descr_file" ] && {
build_functions_sh=jw-build-functions.sh
for d in `dirname $0`; do
[ -f "$d/$build_functions_sh" ] && {
build_functions_sh_path=$d/$build_functions_sh
break
}
done
[ "$build_functions_sh_path" ] || fatal "$build_functions_sh not found"
. $build_functions_sh_path
try_assign_sec $project_descr_file description descr
try_assign_sec $project_descr_file summary summary
try_assign_sec $project_descr_file requires_run requires_run
try_assign_sec $project_descr_file requires_build requires_build
}
[ "$variables" ] && echo -e "$variables"
cat <<-EOT
prefix=$prefix
exec_prefix=\${prefix}
includedir=\${prefix}/include
libdir=\${exec_prefix}/lib
Name: $name
Description: $summary
Version: $version
EOT
[ "$cflags" ] && echo "Cflags: $cflags"
[ "$libflags" ] && echo "Libs: $libflags"
[ "$requires_run" ] && echo "Requires: `cleanup_requires $requires_run`"
[ "$requires_build" ] && echo "Requires.private: `cleanup_requires $requires_build`"
# [ "$requires_devel" ] && ?? # not sure what to do with this
exit 0

View file

@ -1,394 +0,0 @@
export PATH=$PATH:/opt/jwbuild/bin
# -- private stuff not intended for use outside of this script
_jwbuild_check_config_present()
{
if [ -z "$jwbuild_config_files" ]; then
if [ "$1" = true ]; then
jwbuild_log "warning: $0 tries to access configuration witout having a config file"
fi
return 1;
fi
return 0;
}
_jwbuild_check_config_files_readable()
{
local new=""
for f in $jwbuild_config_files; do
[ -f "$f" -a -r "$f" ] && new="$new $f"
done
jwbuild_config_files="$new"
}
_jwbuild_probe_config_paths()
{
local f new exp dir
[ ! "$jwbuild_config_files" ] && jwbuild_config_files="
/etc/opt/$jwbuild_project/$jwbuild_basename.conf
$HOME/.$jwbuild_project/$jwbuild_basename.conf
$HOME/.$jwbuild_project/$rc
$HOME/.$jwbuild_basename/$jwbuild_basename.conf
$HOME/.$jwbuild_basename/$rc
$HOME/.$rc
"
_jwbuild_check_config_files_readable
# add includes
new=""
for f in $jwbuild_config_files; do
exp=`sed '
/^[ ]*include[ ]/ !d
s/^[ ]*include[ ]*//g
' $f`
if [ "$exp" ]; then
if [ "${exp:0:1}" != '/' ]; then
dir=`dirname $f`
exp=$dir/$exp
fi
exp=`ls -d $exp 2>/dev/null`
new="$new $f $exp"
fi
done
[ "$new" ] && jwbuild_config_files="$new"
_jwbuild_check_config_files_readable
}
_jwbuild_cat_section()
{
if [ "$2" ]; then
sed -n "/\[$2\]/,/[^ ]*\[/ p" $1 | sed '/[^ ]*\[/ d; /^[ ]*include[ ]/ d'
return
fi
sed -n '0,/[^ ]*\[/ p' $1 | sed '/[^ ]*\[/ d; /^[ ]*include[ ]/ d'
}
_jwbuild_format_config()
{
sed '/^ *$/d; s/$/;/g' | sed 's/ *= */=/'
}
_jwbuild_format_section()
{
_jwbuild_cat_section $* | _jwbuild_format_config
}
_jwbuild_source_section()
{
eval `_jwbuild_cat_section $* | _jwbuild_format_config`
}
_uniq()
{
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
}
# -- exported utilities
jwbuild_log_err()
{
echo "# $@" >&2
}
jwbuild_log_stdout()
{
echo "# $@"
}
jwbuild_log_stderr()
{
echo "# $@" >&2
}
jwbuild_log_syslog()
{
logger -t $jwbuild_basename "$*"
}
jwbuild_log()
{
local logger
for logger in $jwbuild_loggers; do
eval jwbuild_log_$logger "$@"
done
}
jwbuild_empty_config()
{
local o_verbose=""
local o_section
set -- `getopt 'vs:' $*`
while [ "$1" != -- ]; do
case $1 in
-v)
o_verbose=true;;
-s)
o_section=$2; shift;;
*)
jwbuild_log_err unknown option $1
exit 1;;
esac
shift
done
shift
_jwbuild_check_config_present $o_verbose || return 1
[ ! -r "$1" ] && return
[ "$o_verbose" ] && jwbuild_log o resetting config "\"$1\""
if [ "$o_section" ]; then
[ "$o_verbose" ] && jwbuild_log o resetting section "[$o_section]" of config "\"$1\""
fi
eval `_jwbuild_format_section $1 $o_section | sed 's/=[^;]*;/=;/g'`
}
jwbuild_empty_configs()
{
local section
local c
local o_verbose=""
local o_section
local opts=""
set -- `getopt 'vs:' $*`
while [ "$1" != -- ]; do
case $1 in
-v)
o_verbose=true
opts="$opts -v"
;;
-s)
o_section="-s $2"; shift;;
*)
jwbuild_log_err unknown option $1
exit 1;;
esac
shift
done
shift
_jwbuild_check_config_present $o_verbose || return 1
for section in "" `jwbuild_config_sections`; do
for c in $jwbuild_config_files; do
jwbuild_empty_config -s "$section" section $opts $c
if [ -d $c.d ]; then
local dirconfs=`find $c.d -maxdepth 1 -type f`
local f
for f in $dirconfs; do
jwbuild_empty_config -s "$section" $opts $f
done
fi
done
done
return 0
}
jwbuild_source_config()
{
local o_verbose=""
local o_section
set -- `getopt 'vs:' $*`
while [ "$1" != -- ]; do
case $1 in
-v)
o_verbose=true
;;
-s)
o_section=$2; shift;;
*)
jwbuild_log_err unknown option $1
exit 1;;
esac
shift
done
shift
_jwbuild_check_config_present $o_verbose || return 1
if [ "$o_verbose" = true ]; then
if [ "$o_section" ]; then
jwbuild_log o sourcing section "[$o_section]" from config file "\"$1\""
else
jwbuild_log o sourcing config "\"$1\""
fi
_jwbuild_cat_section $1 $o_section
fi
_jwbuild_source_section $1 $o_section
}
jwbuild_source_configs()
{
local c
local source_opts=""
local o_verbose=""
set -- `getopt 'vs:' $*`
while [ "$1" != -- ]; do
case $1 in
-v)
o_verbose=true
source_opts="$source_opts -v"
;;
-s)
source_opts="$source_opts -s $2"
shift;;
*)
jwbuild_log_err unknown option $1
exit 1;;
esac
shift
done
shift
_jwbuild_check_config_present $o_verbose || return 1
for c in $jwbuild_config_files; do
jwbuild_source_config $source_opts $c
if [ -d $c.d ]; then
local dirconfs=`find $c.d -maxdepth 1 -type f`
local f
for f in $dirconfs; do
jwbuild_source_config $source_opts $f
done
fi
done
}
jwbuild_config_sections()
{
local o_verbose=""
local o_section
local o_file
local o_exact=false
OPTIND=1
while getopts ves:f: flag; do
case $flag in
v)
o_verbose=true;;
s)
o_section="$OPTARG";;
f)
o_file="$OPTARG";;
e)
o_exact=true;;
*)
jwbuild_log_err "unknown option -$flag"
exit 1;;
esac
done
shift $(($OPTIND - 1))
_jwbuild_check_config_present $o_verbose || return 1
[ "$o_file" ] || o_file="$jwbuild_config_files"
if [ "$o_exact" = false ]; then
if [ "$o_section" ]; then
sed "
/^[ ]*\[$o_section\.\(.*\)\]/ !d
s/^[ ]*\[$o_section\.\(.*\)\].*/\1/
" $o_file 2>/dev/null | _uniq
else
sed "
/^[ ]*\[\(.*\)\]/ !d
s/^[ ]*\[\(.*\)\].*/\1/
" $o_file 2>/dev/null | _uniq
fi
else
if [ "$o_section" ]; then
sed "
/^[ ]*\[$o_section\.\([^.]*\)\]/ !d
s/^[ ]*\[$o_section\.\([^.]*\)\].*/\1/
" $o_file 2>/dev/null | _uniq
else
sed "
/^[ ]*\[\([^.]*\)\]/ !d
s/^[ ]*\[\([^.]*\)\].*/\1/
" $o_file 2>/dev/null | _uniq
fi
fi
return
}
jwbuild_cat()
{
cat | sed 's/^[ ]*|//'
}
jwbuild_waitpid()
{
local pid=$1
local t=5
[ "$2" ] && t=$2
while ((t > 0)); do
[ -f /proc/$pid/status ] || {
wait $pid
return 0
}
sleep 1
((t -= 1))
done
return 1
}
jwbuild_terminate()
{
local pid
for pid in $@; do
[ -f /proc/$pid/status ] && {
echo terminating process $pid
kill $pid
jwbuild_waitpid $pid || {
echo "failed to normally terminate process $pid, killing it" >&2
kill -9 $pid
jwbuild_waitpid $pid || {
echo "failed to kill process $pid" >&2
return 1
}
}
}
done
return 0
}
# -- exported variables
if [ "$jwbuild_functions_sourced" != "yes" ]; then
jwbuild_functions_sourced="yes"
[ -z "$jwbuild_basename" ] && jwbuild_basename=`echo "$0" | sed 's/^-*//' | xargs basename | sed 's/\.sh$//'`
[ -z "$jwbuild_project" ] && jwbuild_project="jwbuild"
[ -z "$jwbuild_rc" ] && jwbuild_rc="$jwbuild_basename"rc
_jwbuild_probe_config_paths
[ -z "$jwbuild_loggers" ] && jwbuild_loggers="stdout"
fi

View file

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
import textwrap
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
class CmdCreatePkgConfig(Cmd): # export
def __init__(self) -> None:
super().__init__('create-pkg-config', help='Generate a pkg-config file for a module')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument('-F', '--project-descr-file', default=None)
parser.add_argument('-d', '--description', default=None)
parser.add_argument('-n', '--name', default=None)
parser.add_argument('-s', '--summary', default=None)
parser.add_argument('-p', '--prefix', default=None)
parser.add_argument('-v', '--version', default=None)
parser.add_argument('-c', '--cflags', default=None)
parser.add_argument('-l', '--libflags', default=None)
parser.add_argument('-r', '--requires_run', default=None)
parser.add_argument('-R', '--requires_build', default=None)
parser.add_argument('-V', '--variables', nargs='*')
def _run(self, args: Namespace) -> None:
project_conf_var_keys = ['description', 'summary', 'requires_run', 'requires_build']
merged: dict[str, str] = {}
for key in project_conf_var_keys:
val = getattr(args, key)
if val is not None and args.project_descr_file:
val = self.app.get_value(args.name, key, None)
merged[key] = val
contents = textwrap.dedent(f"""\
prefix={args.prefix}
exec_prefix={{prefix}}
includedir={{prefix}}/include
libdir={{exec_prefix}}/lib
Name: {args.name}
Description: {merged['summary']}
Version: {args.version}
""")
if args.cflags is not None:
contents += f"Cflags: {args.cflags}\n"
if args.libflags is not None:
contents += f"Libs: {args.libflags}\n"
if merged['requires_run'] is not None:
contents += f"Requires: {cleanup_requires(merged['requires_run'])}"
if merged['requires_build'] is not None:
contents += f"Requires.private: {cleanup_requires(merged['requires_build'])}"
# not sure what to do with requires_devel
print(contents)