Okay boys and girls, I've refined my conditional build batch file(s) to the point where they work well and thought I'd contribute them for the benefit of others here. I've found a few solutions to problems in this forum and want to pay it forward.
The major reason for this exercise in the first place was to keep my targets up to date without generating unnecessary builds. If you use Flare's batch targets and schedule them to occur nightly, every project gets rebuilt every night which, in itself isn't a bad thing, but it makes delivery or review decisions based only on changed files more difficult. Plus, if your documentation library is fairly big, it can take a lot of time and processing power to rebuild everything nightly. That's more time and opportunity for something to go wrong and unnoticed. Conditional builds keep it to a minimum. Work smarter, not harder is my motto!
A few caveats are in order first, followed by high-level explanations of each batch file.
1. These batch files WILL need to be modified to suit your project folder structure, requirements, etc. This method relies on all of the projects residing in folders at the same level. See the explanation below for more details. The batch files also need to reside in the same folder together or the calls need to modified to include appropriate paths.
2. No error checking is implemented at this time. You can add your own. Reviewing the output log file reduces the need for it, but to follow good programming practice, it should be added. If I get a round tuit, I'll post updates here.
3. I schedule this batch file to run nightly, resulting in only my modified projects being freshly built the next morning without unnecessary builds of unchanged projects. I follow it with a weekly batch file to archive all of the results into a RAR file and upload it to our development teams overseas. They can then easily see what deliverables have changed and which have not based on the file dates and times.
On with the show! The first batch file establishes a home directory. This directory is the parent of the project folders that will be processed and stores the output log file. The batch file then reads a simple list of project subfolder names from a text file and iterates through each subfolder, calling a second batch file that performs the checking of files for that project. The second batch file is also separate so that I can run it from a command line or other batch file to check an individual project.
Code: Select all
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ::
:: CheckAllBuilds.cmd ::
:: ::
:: Batch file to check the build status of a list of ::
:: Flare projects contained in Projects.txt ::
:: ::
:: Created by Ken Billing ::
:: ::
:: Calls CheckBuild.cmd with each project name ::
:: Logs results in CheckAllBuilds.log ::
:: ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@ECHO Off
:: Set CMD window title
TITLE CheckAllBuilds
SETLOCAL ENABLEDELAYEDEXPANSION
SET HomeDir=%CD%
:: Start new log file
ECHO %DATE% %TIME% Check of all builds started. > "%HomeDir%"\CheckAllBuilds.log
ECHO HomeDir = %HomeDir% >> "%HomeDir%"\CheckAllBuilds.log
FOR /F "usebackq delims==" %%P in (Projects.txt) DO (
ECHO ============================================================== >> "%HomeDir%"\CheckAllBuilds.log
ECHO !DATE! !TIME! Checking "%%P". >> "%HomeDir%"\CheckAllBuilds.log
CALL CheckBuild.cmd "%%P"
)
ECHO %DATE% %TIME% Check of all builds finished. >> "%HomeDir%"\CheckAllBuilds.log
The second batch file does the heavy lifting. It initializes several environment variables to store the newest target and topic file names and dates/times. It then finds the newest topic file in subfolders of the Content folder. This batch file does not look for other types of updated files, such as images or TOCs. Those could be added to the process using the same technique if you often change those files independent of your topic files.
IMPORTANT: All of my topic files reside in subfolders of Content except topics (Table of Contents, Glossary, Index, etc. master pages) that are used commonly across all projects. To process files residing directly in Content requires modification of this batch file accordingly.
It then proceeds to find the newest CHM target file. Although I also generate other targets in my projects, I key off of the CHM file since it's our primary deliverable. Your situation may be different. Note that you will also need to substitute your user name for "Ken_Billing" in the batch file. I use the same target name for this CHM file in every project too, "Microsoft HTML Help" so that the batch file applies equally well to every project.
Lastly, it compares the dates and times of the newest topic and target files that it found. If a topic is newer than the target, it calls a third batch file to rebuild that project. Two subroutines can be found at the bottom of the file, that are called for every file that the batch file examines. Note that I store the date/time values as single number in the output log file. This actually makes them easier to read and compare. Alternative display statements are provided if you want to use them, but are commented out.
Code: Select all
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ::
:: CheckBuild.cmd ::
:: ::
:: Batch file to check the date of the current HTML Help ::
:: target against current topics files for one Flare ::
:: project. ::
:: ::
:: Created by Ken Billing ::
:: ::
:: Called from CheckAllBuilds.cmd ::
:: Accepts project name read from Projects.txt ::
:: Calls MakeBuild.cmd with same project name ::
:: Logs results in CheckAllBuilds.log ::
:: ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@ECHO On
:: Set CMD window title
TITLE CheckBuild %1%
SETLOCAL ENABLEDELAYEDEXPANSION
SET NewestTopic=
SET NewestTopicDate=0
SET Result=0
CD %1%
:: Test for presence of build output to check
IF EXIST Output\Ken_Billing\Microsoft HTML Help\*.chm (
:: File must be in current folder to get the date
CD Content
:::::::::::::::::::::::::::::::::::::
:: Get newest topic filename and date
FOR /D %%D IN (*.) DO (
ECHO Checking topics in %%D >> "%HomeDir%"\CheckAllBuilds.log
CALL :CompareTopicDates "%%D"
CD %%D
)
)
CD %HomeDir%
ECHO Newest topic = %NewestTopic% >> "%HomeDir%"\CheckAllBuilds.log
ECHO Newest topic date = %NewestTopicDate% >> "%HomeDir%"\CheckAllBuilds.log
::Alternate parsing for friendly date display
::%NewestTopicDate:~4,2%-%NewestTopicDate:~6,2%-%NewestTopicDate:~0,4% %NewestTopicDate:~8,2%:%NewestTopicDate:~10,2% >> "%HomeDir%"\CheckAllBuilds.log
::::::::::::::::::::::::::::::::::::::
:: Get newest target filename and date
:: File must be in current folder to get the date
CD %1%
CD Output\Ken_Billing\Microsoft HTML Help
SET NewestTarget=
SET NewestTargetDate=0
IF EXIST *.chm (
FOR /F "usebackq delims==" %%F IN (`DIR /B /T:W /O:D "*.chm"`) DO (
SET NewestTarget=%%F
CALL :FileDateToString "%%F"
)
)
SET NewestTargetDate=%Result%
CD %HomeDir%
ECHO Newest target date = %NewestTargetDate% >> "%HomeDir%"\CheckAllBuilds.log
::Alternate parsing for friendly date display
::%NewestTargetDate:~4,2%-%NewestTargetDate:~6,2%-%NewestTargetDate:~0,4% %NewestTargetDate:~8,2%:%NewestTargetDate:~10,2% >> "%HomeDir%"\CheckAllBuilds.log
:::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Compare dates as strings because they are too long
:: to compare as numbers
IF "%NewestTopicDate%" GTR "%NewestTargetDate%" (
CALL MakeBuild.cmd %1%
) ELSE (
ECHO %DATE% %TIME% Targets are up to date. >> "%HomeDir%"\CheckAllBuilds.log
)
:: Report errors
::IF %ERRORLEVEL% GTR 0 ECHO An error occurred!
GOTO :EOF
::::::::::::::::::::::::::::::
: COMPARETOPICDATES
:: Compares the file dates of all HTM files in a given folder against
:: the date of the file saved in the NewestTopic env variable and
:: if one is newer, updates the variables with the newer file name and date
::ECHO Entered CompareTopicDates! >> CheckAllBuilds.log
CD %1%
FOR /F "usebackq delims==" %%F IN (`DIR /B /T:W /O:D "*.htm"`) DO (
CALL :FileDateToString "%%F"
IF "!Result!" GTR "!NewestTopicDate!" (
SET NewestTopic=%%F
)
:: Cannot execute multiple statements in nested IF, so evaluate
:: the second statement separately
IF "!Result!" GTR "!NewestTopicDate!" (
SET NewestTopicDate=!Result!
)
)
CD ..
GOTO :EOF
:::::::::::::::::::::::::::::
: FILEDATETOSTRING
:: Parses user-friendly MM/DD/YYYY HH:MM AP strings into comparable strings
:: and converts 12 hour format time to 24 hour format
:: Get friendly file date string
SET Result=
SET FileDate=%~t1
:: Parse out hour
SET Hour=%FileDate:~11,2%
:: If AM, remove the zero. Bug.
:: IF %Hour:~0,1% == 0 SET Hour=%Hour:~1,1%
:: If PM, convert to 24 hour format
IF "%FileDate:~17,1%" == "P" SET /a Hour=%Hour%+12
:: Concatenate strings and save the result
SET Result=%FileDate:~6,4%%FileDate:~0,2%%FileDate:~3,2%%Hour%%FileDate:~14,2%
:: ECHO Result is: %Result%
GOTO :EOF
Last, but not least, is the batch file to perform the actual build. This file is also separate so that I can execute it to build an individual project. This one is relatively simple compared to the last and is also similar. It uses the DIR command to find the file name of the Flare project, which can be different from the folder name stored in Projects.txt. It executes madbuild by passing it the project file name with the -batch switch and the batch target named Build All that exists in every project. In my case, Build All builds all of the targets, not just the CHM. You can use the batch target name here, supply your own preferred batch target name, or change the batch file to build a single target.
This batch file stores its output in a separate log file for each project since there is so much info produced. This keeps the main output log file smaller and more readable. You can check individual builds by reviewing the individual log files.
Code: Select all
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ::
:: MakeBuild.cmd ::
:: ::
:: Batch file to automate building ::
:: one Flare target ::
:: ::
:: Created by Ken Billing ::
:: Based on an example by Steve Salter ::
:: ::
:: Called from CheckBuild.cmd ::
:: Accepts project name read from Projects.txt ::
:: Logs results in <ProjectName>_Build.log ::
:: ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@ECHO On
:: Set CMD window title
TITLE MakeBuild %1%
SETLOCAL
:: Log start of processing
ECHO %DATE% %TIME% Building batch target Build All for %1% >> CheckAllBuilds.log
:: Change to project directory
CD %1%
FOR /F "delims=" %%I IN ('DIR *.flprj /B') DO (
"C:\Program Files\Madcap Software\Madcap Flare V6.1\Flare.app\madbuild" -project "%%I" -batch "Build All"> %%I_Build.log
)
CD %HomeDir%
:: Add build completion line to log
ECHO %DATE% %TIME% Targets of Build All built for %1% >> CheckAllBuilds.log
That's it!
These are provided as-is, without support, and as examples for building your own solutions. I'll try to answer general questions about hows and whys, but have a day job that requires most of my attention. I'm sure you all understand.
Flare itself might provide an option in batch targets someday to only build stale targets, so this could become obsolete. Until then, it works good for me!
Enjoy,
Ken