#!/bin/sh
cat << 'EEE' > /dev/null
/ * Copyright (C) 2021 Momi-g
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
EEE

usage(){
cat << 'EEE'
HowTo (mglb, merge lib/obj by collecting only dependent objfiles. GPLv3+)
opt: -h, -H(quick guide), -p(osix, dont edit symbol)	needs:nm, objcopy@gnu
------
eg) ~$ mglb yourlib.a libXX.a libYY.a    # ag1:obj requester ag2-:obj supplier
		#>> outlib.a
    ~$ mglb yourobj.o libXX.a libZZ.o    # allow filetype: *.a / *.o
		#>> outlib.a

  your.a/o       libXX.a/o       libYY.a/o            outlib.a             
   aa.o(dp AA.o)  AA.o(dp CCC.o)  AAA.o                1:aa.o  4:BB.o(libYY)
   bb.o(dp BB.o)  BB.o            BB.o (not typo)      2:AA.o  5:bb.o  7:cc.o
   cc.o(dp AAA.o) CC.o	          CCC.o(dp self BB.o)  3:CCC.o 6:BB_.o 8:AAA.o 	
                                                         
 - search order: self >> ag1 >> ag2 >> ...
 - edit: inner depenent symbol may be renamed to avoid collision if -p isnt set.
  raise err and exit 1 if detect sym collision under the -p mode.
 
   libYY.a/o  outlib.a           : required sym(f3) is saved. inner dependant-
    AAA.o       -                : global symname is changed.
    BB.o (f2)  BB.o(libYY_BB_f2)  libYY.a :CCC.o: f3(){ ..int rc = f2() ... }
    CCC.o(f3)  CCC.o(f3)          outlib.a:CCC.o: f3(){..rc = libYY_BB_f2()..}
EEE
	exit 0
}

usage_H(){
cat << 'EEE'
--quick guide 
 there is two fat libs
	libgoogle.a (100MB) api: f1(),f2(), ...regex(), ...	
	libamazon.a (200MB) api: a1(),b1(),f1()...calc(), ...	
 and you want to make wrapper lib, myregex() + mycalc()
	libmyutil.a (1MB) api: myregex(), mycalc()

 1. write wrapper src
  //src.c
  char* regex(char* s);      //from libgoogle
  int   calc(int a, int b);  //from libamazon
 
  char* myregex(char* s){ return regex(s); }
  int   mycalc(int a, int b){ return calc(a,b); }
 
 2. make lib
  ~$ cc -c src.c		#>> src.o
  ~$ ar -r libmyutil.a src.o		#>>libmyutil.a
 
 3. merge. mglb collects and adjusts implement objfiles
  ~$ mglb libmyutil.a	libgoogle.a  libamazon.a
  (...or ~$ mglb src.o	libgoogle.a  libamazon.a )
  #>> outlib.a
 
 4. finish. check data
  ~$ nm outlib.a


-- bollow api from other pkg
 1. build other pkg (not install)
  ~$ ./configure
  ~$ make
 
 2. collect XXX.o files
  ~$ buf=`find ./ -type f|grep '[.]o$'`
  ~$ ar -r libtmp.a $buf
 
 3. take out impl objfiles from libtmp.a
  ~$ mglb yoursrc.o libtmp.a

--hint
 you may need to edit lib/obj symbols. gnu util 'objcopy' will be useful.
   ~$ objcopy -N main src.o	#>> remove 'main' symbol(==func) from src.o
   ~$ objcopy -L f1 src.o	#>> change f1 symbol to local(static) value
   ~$ objcopy --globalize-symbol f1 src.o  #>> change f1 symbol to global
   ~$ objcopy --redefine-sym f1=g1 src.o   #>> change f1 symname to g1
EEE
	exit 0
}

tmpdir=""
err(){
echo "$*" >/dev/stderr;
cd "$cdir"
[ "$tmpdir" = "" ]||rm -rf "$tmpdir"
exit 1
}
pl()(printf '%s\n' "$@")

addobj(){
	# cwd == /tmp/XXX + *.o exists
	addlsv="$1"	# req orgsyms list(3fld): sym lib_file_sym.o lib_file_(pfix)
	#libは.aならつくし.oならつかない
	buf=""
	# add pfix(libXX_file_) if supply is *.a
	fpfix=`pl "$addlsv"|awk '{print $3}'|head -n 1`	#pfixの加工 誰でもいい
	lpfix="${fpfix%_*_}_"

	#下でpfix系だけど.oは_headになるのでよろしくない。空っぽにしておく
	[ "$bi" = "${fpfix%?}.o" ]&&lpfix=""
# .a系出身はlib_file.oにrename	>>止めた 必要なら最後で
#	for bi in `ls *.o`;do
#		[ "$bi" = "${fpfix%?}.o" ]&&break	#.o単体はfld3の_oがfileと一致するのでスキップ
#		mv "$bi" "$lpfix$bi"
#	done


# --sym rename
	# all [^U] sym/file/pfix list. rename target
	buf=`ls *.o`
	buf=`nmgr '[^U]' $buf|sort -u`
	# remove orgsym from list and change 3fld >> 2fld, new old rep
	buf=`(cllr "$addlsv" "$buf")|sort|uniq -u|awk '{print $1 " " $1 " " $3 $1}'`
	# remove '_' system name sym from list
	addsym=`pl "$buf"|grep -v ^_`
	booked=`nmgr '[^U]' $cdir/outlib.a|sort -u|awk '{$3="";print}'`
	# cmpr symname coll and add '_' suffix if hit
	while :;do
		ck=`cll "$booked" "$addsym"`
		[ ${#ck} = 0 ]&&break
		[ $pmode = 1 ]&&err "ERR: detect symbol collision under posix mode: "$ck

		sf=`cllr "$booked" "$addsym"|sort -u`	# sv no coll
		buf=`pl "$ck"|awk '{$1=$3;$3=$3 "_";print $0}'`	# add suffix if hit
		addsym=`pl "$buf" "$sf"|sort -u`	# bind + loop until uniq all
	done
	optstr=`pl "$buf"|awk '{print "--redefine-sym " $2 "=" $1}'|sort -u|tr '\n' ' '`

	# adopt new symname to all objfiles
	ar -r outlib_.a *.o
	[ $pmode = 0 ] && objcopy $optstr outlib_.a
	ar -x outlib_.a
	rm outlib_.a
	echo "del outlib_.a" >/dev/stderr

	# add. edit file.o name if detect collision
	for bi in *.o;do
		add="$lpfix"
		bfile="$bi"
		while :;do
			buf=`ar -t "$cdir/"outlib.a "$bi" 2>/dev/null`
			[ ${#buf} = 0 ]&&break
			if [ "$add" = "" ];then
				while [ -f $bi ];do bi="${bi%.o}_.o"; done	#変更が先住民を潰すかも
				mv $bfile $bi
				bfile=$bi
				continue
			fi
			bi="$add$bi"
			add=""
			while [ -f $bi ];do bi="${bi%.o}_.o"; done
			mv $bfile $bi
			bfile=$bi
		done
		ar -r "$cdir/"outlib.a "$bi"
	done
	rm *.o
}

# name fid の二項目ならなる ar1 ag2をuniq-dして $1が衝突ならag2を出力
# uq1 ag1 ag2 #>> myf xxx.a[fc.o] 
cll()(pl "$1" "" "$2"| awk 'NF==0{m=1} m==0{ar[$1]=1;next} $1 in ar{print $0}')
cllr()(pl "$1" "" "$2"|awk 'NF==0{m=1} m==0{ar[$1]=1;next} !($1 in ar){print $0}')

# nmgr [^U] *.o	...lib.aからTの fcname fname lib.aobj.oを吐く
# $1はfld3のタイプ指定 基本はUかそれ以外のg系のみ扱う
# グローバルシンボルで未定義とここで定義の切り分けと抽出
nmgr(){
tp="$1";shift
nm "$@" -AgP|awk -v tp="$tp" '$3~tp{print $2 " " $1}'|
#grep -v ^_|
sort -u|tr '][:' '@@ '|sed -e 's/\([^[:blank:]@]*\)@\([^@]*\)@/\2 \1_\2/g'|
sed -e 's/[.][ao]//g'|awk 'NF==2{$0=$0 " " $2}{$2=$2 ".o";$3=$3 "_"}{print}'
}
#最初にtgtのobj.aoからUをリストする _系の関数名は避ける
#tgtがaだったらTを調べて存在すればUから削除

# --main
pmode=0
[ $# = 0 ]&&usage
for i;do
	shift
	[ "$i" = "-h" ]&&usage
	[ "$i" = "-H" ]&&usage_H
	[ "$i" = "-p" ]&&pmode=1&&continue
	set -- "$@" "$i"
done

#--test/init
nm -h>/dev/null||err "ERR: nm failed"
cdir=`pwd`||err "ERR: pwd failed"
[ -e outlib.a ]&&[ ! -f outlib.a ]&&err "ERR: outlib.a exists and not regular file"
tmpdir=`mktemp -d`||err "ERR: mktemp failed"
echo "make tmpdir: $tmpdir" >/dev/stderr
[ -f outlib.a ]&&rm outlib.a
[ ${1##*.} = "a" ]&&cp $1 outlib.a
[ ${1##*.} = "o" ]&&ar -r outlib.a $1
shift

#--coreloop $2-
for i;do
	cd "$cdir"
	#search req Usyms
	rq=`nmgr U outlib.a`
	buf=`nmgr '[^U]' outlib.a`
	rq=`cllr "$buf" "$rq"|awk 'NF!=0{print $1}'|sort -u`
	
	#search resolv Tsyms
	buf=`nmgr '[^U]' $i`
	addl=`cll "$rq" "$buf"`	# Tsymslist:  sym  lib.a_file.o

	[ "$addl" = "" ]&&continue
	if [ "$addl" != "" ]&&[ "${1##*.}" = o ]; then
		cp "$i" "$tmpdir"
		cd "$tmpdir"
		addobj "$addl"	# if file.o, add direct
		continue
	fi
	
	# if file.a, search recursive dependance
	addlsv="$addl"	#root request+resolved  syms [^U] 
	while :;do
		olist=`pl "$addl"|awk 'NF!=0{print $2}'|sort -u`	#追加予定の.oリスト
		# 再依存調査のため追加予定.o全部から一括でUを集める
		# .ofileが候補だけど.aなのでarからUフィルタ+fileで全oの全Uをgrepする 
		buf=`nmgr U $i|awk 'NF!=0{print $2 " " $1 " " $3}'`	#.o sym pre
		o_undef=`cll "$olist" "$buf"|awk 'NF!=0{print $2 " " $1 " " $3}'|sort -u`
		#修正して戻す
		
		df=`nmgr '[^U]' $i`	#ar内の全Tリスト
		buf=`cll "$o_undef" "$df"`	# .o U をar Tでフィルタ >>衝突が追加分
		df=`pl "$addl" "$buf"|sort -u`	#break処理の関係で先に追加。NULL行ok
		[ "$df" = "$addl" ]&&break	#サチったら脱出
		addl="$df"
	done
	# addlは sym .o pfixの羅列 .oファイル名のみ取り出す addlも後で使う
	ofile=`pl "$addl"|awk '{print $2}'|sort -u`
# echo "@addl
# $addl
# @"
# echo "#addlsv
# $addlsv"
# echo "$ofile"
# exit
	cp $i $tmpdir
	cd "$tmpdir"
	ar -x $i
	rm `(ls;pl "$ofile")|sort|uniq -u`
	addobj "$addlsv" #svはreqなのでrenameしない奴リスト 追加処理は共通でrename
done

rm -R "$tmpdir"
echo "del tmpdir: $tmpdir" >/dev/stderr



cat<<'EEE'>/dev/null
 change log
 --
2021-04-23  Momi-g	<dmy@dmy.dmy>

	* mglb(all) : add -p opt. fix err. fix help.

EEE
