d87ef2fb8472135b899fd1a89434033d5895217a
mspeir
  Thu Oct 1 15:31:26 2026 -0700
Add barChartReorder, a utility for changing the order of the bars in a barChart track, refs #37619

The bars of a barChart are drawn in the order their values appear in the
expScores field, and nothing re-sorts them when the track is drawn, so the
order is a property of the data file. Putting the bars in a different order
means rewriting that field in every row, which is awkward enough by hand that
we have twice done it for a data author instead of explaining it. This has
come up on #36998 and #37001.

barChartReorder takes the current bar order and the wanted order as two lists
of names, rewrites expScores in every row, optionally puts a .categories file
into the same order, and prints the barChartBars line for the track. It works
on any barChart BED whatever produced it, so unlike the --groupOrderFile
option of expMatrixToBarchartBed it does not assume the file was built here
from an expression matrix. A bigBarChart is handled by converting it with
bigBedToBed and rebuilding it afterwards, which the usage message spells out.

It refuses to write a half-correct file: the two orders must name the same
bars, every row must hold as many values as there are names, and a .categories
file must have a row for each bar.

Listed in USER_APP_SCRIPTS so it reaches hgdownload, and in the no-argument
branch of mkREADME.sh so the README picks up its usage message rather than the
argparse error that -verbose=2 would produce.

Its tests are also wired into the test target directly. testAll iterates
ALL_APPS, which does not include USER_APP_SCRIPTS, so none of these programs'
tests have ever been reachable from "make test" - that is why the suite sitting
next to expMatrixToBarchartBed never reported it was broken. The makefile
already keeps a short list of directories testAll cannot reach, and this adds
two more to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/utils/makefile src/utils/makefile
index d9e29c8a34a..495d6e7cf96 100644
--- src/utils/makefile
+++ src/utils/makefile
@@ -1,308 +1,312 @@
 # Build all directories in utils
 
 kentSrc = ..
 
 include ${kentSrc}/inc/localEnvironment.mk
 include ${kentSrc}/inc/common.mk
 
 # note: lists such as the following need to be defined *before*
 # they are used in the wildcard target rules.  An oddity of gnu make
 
 # define the list of user applications to build
 # These will be distributed as part of the user apps build and we provide them 
 # as binaries and collect usage messages
 # These are C programs, so make clean will be run
 
 UTILS_APPLIST = \
 	addCols \
 	ave \
 	aveCols \
 	bamToPsl \
 	bedClip \
 	bedGraphToBigWig \
 	bedJoinTabOffset \
 	bedToBigBed \
 	bigBedInfo \
 	bigBedNamedItems \
 	bigBedSummary \
 	bigBedToBed \
 	bedCommonRegions \
 	bedGeneParts \
 	bedGraphPack \
 	bedPileUps \
 	bedRemoveOverlap \
 	bedRestrictToPositions \
 	bigWigAverageOverBed  \
 	bigWigCat \
 	bigWigCorrelate \
 	bigWigCluster \
 	bigWigInfo \
 	bigWigMerge \
 	bigWigSummary \
 	bigWigToBedGraph \
 	bigWigToWig \
 	calc \
 	catDir \
 	catUncomment \
 	chopFaLines \
 	colTransform \
 	countChars \
 	endsInLf \
 	clusterMatrixToBarChartBed \
 	cpgIslandExt \
 	faAlign \
 	fastqToFa \
 	fastqStatsAndSubsample \
 	faCmp \
 	faCount \
 	faFilterN \
 	faFilter \
 	faFrag \
 	faNoise \
 	faOneRecord \
 	faPolyASizes \
 	faRandomize \
 	faRc \
 	faSize \
 	faSomeRecords \
 	faSplit \
 	faToFastq \
 	faToTab \
 	faToTwoBit \
 	faTrans \
 	findMotif \
 	headRest \
 	htmlCheck \
 	matrixClusterColumns \
 	matrixMarketToTsv \
 	matrixNormalize \
 	matrixToBarChartBed \
 	newProg \
 	newPythonProg \
 	nibFrag \
 	nibSize \
 	pafToPsl \
 	paraFetch \
 	paraSync \
 	pslMap \
 	pslMapPostChain \
 	pslPosTarget \
 	pslProtToRnaCoords \
 	pslRc \
 	pslRemoveFrameShifts \
 	pslScore \
 	pslSpliceJunctions \
 	pslSwap \
 	pslToPslx \
 	randomLines \
 	raToTab \
 	raToLines \
 	rmFaDups \
 	rowsToCols \
 	sizeof \
 	spacedToTab \
 	splitFile \
 	splitFileByColumn \
 	strexCalc \
 	stringify \
 	subChar \
 	subColumn \
 	tabFmt \
 	tailLines \
 	textHistogram \
 	tickToDate \
 	timing \
 	toLower \
 	toUpper \
         trackDbIndexBb \
 	twoBitDup \
 	twoBitInfo \
 	twoBitToFa \
 	wigCorrelate \
 	wigToBigWig \
 	wordLine \
 
 # list of utilities to build of interest only at UCSC
 # These are C tools and will run a make clean on each
 # These are not part of the user apps build.
 
 UCSC_APPS = \
 	addCols \
 	aNotB \
 	aveCols \
 	ccCp \
 	convolve \
 	detab \
 	dnsInfo \
 	faFlyBaseToUcsc \
 	faGapLocs \
 	faGapSizes \
 	faNcbiToUcsc \
 	faRenameRecords \
 	faSimplify \
 	faToNib \
 	faTrimPolyA \
 	faTrimRead \
 	fixCr \
 	gapSplit \
 	gitTools \
 	gffPeek \
 	git-reports \
 	htmlPics \
 	jkUniq \
 	lineCount \
 	nt4Frag \
 	pslToXa \
 	scrambleFa \
 	subs \
 	tabToMmHash \
 	tableSum \
 	textHist2 \
 	udcCleanup \
 	undupFa \
 	upper \
 	vcfFilter \
 	venn \
 	verticalSplitSqlTable \
 	weedLines
 
 # These are parts of the user apps build, so they will land on hgdownload
 # These are not C programs, so no clean up
 
 USER_APP_SCRIPTS = \
 	bedJoinTabOffset.py \
 	expMatrixToBarchartBed \
+	barChartReorder \
 	barChartMaxLimit \
 	ucscApiClient \
 	tdbRename \
 	tdbSort \
 	webSync \
 	chromToUcsc \
 	bigHeat \
 	bigGuessDb \
 	hubtools
 
 # Scripts that are not distributed outside of UCSC
 # They are not part of the user-apps build
 SCRIPT_FILES = \
 	agpToLift \
 	apacheLogParse \
 	bedCollapse.pl \
 	bedFixBlockOverlaps \
 	bedOverlapMerge \
 	bedSingleCover.pl \
 	bedBetween \
 	doLocusName \
 	extToolsChromeParse \
 	iSync \
 	lodToBedScore \
 	matrixMerge \
 	mkMafFrames.pl \
 	patSeqFilterBulkAndAnnotate \
 	patSeqSummarizeDocs \
 	pslProtCnv \
 	pslSameGene \
 	tabRepl \
 	uniprotToTab \
 	ggTables \
 	ggGeneClasses \
 	ggGpmlToTab \
 	ggKgmlToTab \
 	ggMsrToTab \
 	ggPidToTab \
 	ggPpiToTab \
 	genePredCompare \
 	ggSpfToTab \
 	bedNamePickFirst \
 	bedBestTranscript \
 	genePredToBed \
 	cgiSmokeTest \
 	phegenToBed \
 	bigMaxPercDiff \
 	cancerMafToBigBed \
 	pq \
 	bedAppendPaperInfo \
 	googleSheetToBigBed \
 	doClinvarLift \
 	codeReviewAi.py \
 	redmineCli \
 	browserRace \
 	ts \
 	${USER_APP_SCRIPTS}
 
 # List of all directories to build
 
 ALL_APPS = $(UTILS_APPLIST) $(UCSC_APPS)
 
 # all: builds all utilities here
 
 all: $(ALL_APPS:%=%.all) $(USER_APP_SCRIPTS:%=%.userAppScript)
 
 %.all:
 	cd $* && echo $* && $(MAKE)
 
 # userApps: builds the user application specific programs
 .PHONY: userApps
 userApps: $(UTILS_APPLIST:%=%.userApp) $(USER_APP_SCRIPTS:%=%.userAppScript)
 
 %.userApp:
 	cd $* && echo $* && $(MAKE)
 
 %.userAppScript:
 	@rm -f ${DESTDIR}${BINDIR}/$*
 	@if test -d $* ; \
 	then \
 	    chmod +x $*/$*; \
 	    echo cp -p $*/$* ${DESTDIR}${BINDIR}; \
 	    cp -p $*/$* ${DESTDIR}${BINDIR}; \
 	else \
 	    chmod +x $*; \
 	    echo cp -p $*/$* ${DESTDIR}${BINDIR}; \
 	    cp -p $* ${DESTDIR}${BINDIR}/$*; \
 	fi
 
 scripts: $(SCRIPT_FILES:%=%.scripts)
 
 %.scripts:
 	@rm -f ${SCRIPTS}/$*
 	@if test -d $* ; \
 	then \
 	    chmod +x $*/$*; \
 	    echo cp -p $*/$* ${SCRIPTS}; \
 	    cp -p $*/$* ${SCRIPTS}; \
 	else \
 	    chmod +x $*; \
 	    echo cp -p $* ${SCRIPTS}; \
 	    cp -p $* ${SCRIPTS}; \
 	fi
 
 testAll: $(ALL_APPS:%=%.testAll)
 
 %.testAll:
 	@test -s $*/tests/makefile && (echo $*; cd $* && $(MAKE) test) || true
 
 test: testAll
 	cd faToTwoBit && ${MAKE} test
 	cd twoBitToFa && ${MAKE} test
 	cd twoBitInfo && ${MAKE} test
 	cd bedGeneParts && ${MAKE} test
 # testRegistry is not an app, so the testAll rule above cannot reach it.  Its test checks
 # that every row of the ticket-to-test registry still names a test that is in the tree,
 # refs #38391.
 	cd testRegistry && ${MAKE} test
+# USER_APP_SCRIPTS are not in ALL_APPS either, so testAll cannot reach their tests.
+	cd barChartReorder && ${MAKE} test
+	cd expMatrixToBarchartBed && ${MAKE} test
 
 clean: $(ALL_APPS:%=%.clean)
 	touch non-empty-rm.o
 	- find . -name \*.o -print | xargs rm
 
 %.clean:
 	cd $* && echo $* && $(MAKE) clean
 
 BAD_DIRS= \
 	ccCpLock \
 	est2genomeToPsl \
 	geneStarts \
 	jimgrep \
 	jkShell \
 	wigAsciiToBinary \
 	cCp