# -*- tcl -*- # Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from -buildversion.txt # # Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. # Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # (C) 2023 # # @@ Meta Begin # Application punk::mix::commandset::doc 999999.0a1.0 # Meta platform tcl # Meta license # @@ Meta End # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Requirements ##e.g package require frobz package require punk::path ;# for treefilenames, relative package require punk::repo package require punk::docgen ;#inline doctools - generate doctools .man files at src/docgen prior to using kettle to producing .html .md etc package require punk::mix::cli ;#punk::mix::cli::lib used for kettle_call #package require punk::mix::util ;#for path_relative # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::mix::commandset::doc { namespace export * proc _default {} { puts "documentation subsystem" puts "commands: doc.build" puts " build documentation from src/doc to src/embedded using the kettle build tool" puts "commands: doc.status" } proc build {} { puts "build docs" set projectdir [punk::repo::find_project] if {$projectdir eq ""} { puts stderr "No current project dir - unable to build docs" return } #user may delete the comment containing "--- punk::docgen::overwrites" and then manually edit, and we won't overwrite #we still generate output in src/docgen so user can diff and manually update if thats what they prefer set oldfiles [punk::path::treefilenames -dir $projectdir/src/doc _module_*.man] foreach maybedoomed $oldfiles { set fd [open $maybedoomed r] chan conf $fd -translation binary set data [read $fd] close $fd if {[string match "*--- punk::docgen overwrites *" $data]} { file delete -force $maybedoomed } } set generated [lib::do_docgen modules] if {[dict get $generated count] > 0} { #review set doclist [dict get $generated docs] set source_base [dict get $generated base] set target_base $projectdir/src/doc foreach dinfo $doclist { lassign $dinfo module fpath set relpath [punk::path::relative $source_base $fpath] set relfolder [file dirname $relpath] if {$relfolder eq "."} { set relfolder "" } file mkdir [file join $target_base $relfolder] set target [file join $target_base $relfolder _module_[file tail $fpath]] puts stderr "target --> $target" if {![file exists $target]} { file copy $fpath $target } } } if {[file exists $projectdir/src/doc]} { set original_wd [pwd] cd $projectdir/src #---------- set installer [punkcheck::installtrack new project.new $projectdir/src/.punkcheck] $installer set_source_target $projectdir/src/doc $projectdir/src/embedded set event [$installer start_event {-install_step kettledoc}] #use same virtual id "kettle_build_doc" as project.new - review best way to keep identifiers like this in sync. $event targetset_init VIRTUAL kettle_build_doc ;#VIRTUAL - since there is no specific target file - and we don't know all the files that will be generated $event targetset_addsource $projectdir/src/doc ;#whole doc tree is considered the source #---------- if {\ [llength [dict get [$event targetset_source_changes] changed]]\ } { $event targetset_started # -- --- --- --- --- --- puts stdout "BUILDING DOCS at $projectdir/src/embedded from src/doc" if {[catch { if {"::meta" eq [info commands ::meta]} { puts stderr "There appears to be a leftover ::meta command which is presumed to be from doctools. Destroying object" ::meta destroy } punk::mix::cli::lib::kettle_call lib doc #Kettle doc } errM]} { $event targetset_end FAILED -note "kettle_build_doc failed: $errM" } else { $event targetset_end OK } # -- --- --- --- --- --- } else { puts stderr "No change detected in src/doc" $event targetset_end SKIPPED } $event end $event destroy $installer destroy cd $original_wd } else { puts stderr "No doc folder found at $projectdir/src/doc" } } proc status {} { set projectdir [punk::repo::find_project] if {$projectdir eq ""} { puts stderr "No current project dir - unable to check doc status" return } if {![file exists $projectdir/src/doc]} { set result "No documentation source found. Expected .man files in doctools format at $projectdir/src/doc" return $result } set original_wd [pwd] cd $projectdir/src puts stdout "Testing status of doctools source location $projectdir/src/doc ..." flush stdout #---------- set installer [punkcheck::installtrack new project.new $projectdir/src/.punkcheck] $installer set_source_target $projectdir/src/doc $projectdir/src/embedded set event [$installer start_event {-install_step kettledoc}] #use same virtual id "kettle_build_doc" as project.new - review best way to keep identifiers like this in sync. $event targetset_init QUERY kettle_build_doc ;#usually VIRTUAL - since there is no specific target file - and we don't know all the files that will be generated - but here we use QUERY to ensure no writes to .punkcheck set last_completion [$event targetset_last_complete] if {[llength $last_completion]} { #adding a source causes it to be checksummed $event targetset_addsource $projectdir/src/doc ;#whole doc tree is considered the source #---------- set changeinfo [$event targetset_source_changes] if {\ [llength [dict get $changeinfo changed]]\ } { puts stdout "changed" puts stdout $changeinfo } else { puts stdout "No changes detected in $projectdir/src/doc tree" } } else { #no previous completion-record for this target - must assume changed - no need to trigger checksumming puts stdout "No existing record of doc build in .punkcheck. Assume it needs to be rebuilt." } $event destroy $installer destroy cd $original_wd } proc validate {args} { set argd [punk::args::get_dict { -- -type none -optional 1 -help "end of options marker --" -individual -type boolean -default 1 *values -min 0 -max -1 patterns -default {*.man} -type any -multiple 1 } $args] set opt_individual [tcl::dict::get $argd opts -individual] set patterns [tcl::dict::get $argd values patterns] #todo - run and validate punk::docgen output set projectdir [punk::repo::find_project] if {$projectdir eq ""} { puts stderr "No current project dir - unable to check doc status" return } if {![file exists $projectdir/src/doc]} { set result "No documentation source found. Expected .man files in doctools format at $projectdir/src/doc" return $result } set original_wd [pwd] set docroot $projectdir/src/doc cd $docroot if {!$opt_individual && "*.man" in $patterns} { if {[catch { dtplite validate $docroot } errM]} { puts stderr "commandset::doc::validate failed for projectdir '$projectdir'" puts stderr "docroot '$docroot'" puts stderr "dtplite error was: $errM" } } else { foreach p $patterns { set treefiles [punk::path::treefilenames $p] foreach path $treefiles { puts stdout "dtplite validate $path" dtplite validate $path } } } #punk::mix::cli::lib::kettle_call lib validate-doc cd $original_wd } namespace eval collection { variable pkg set pkg punk::mix::commandset::doc namespace export * namespace path [namespace parent] } namespace eval lib { variable pkg set pkg punk::mix::commandset::doc proc do_docgen {{project_subpath modules}} { #Extract doctools comments from source code set projectdir [punk::repo::find_project] set output_base [file join $projectdir src docgen] set codesource_path [file join $projectdir $project_subpath] if {![file isdirectory $codesource_path]} { puts stderr "WARNING punk::mix::commandset::doc unable to find codesource_path $codesource_path during do_docgen - skipping inline doctools generation" return } if {[file isdirectory $output_base]} { if {[catch { file delete -force $output_base }]} { error "do_docgen failed to delete existing output base folder: $output_base" } } file mkdir $output_base set matched_paths [punk::path::treefilenames -dir $codesource_path -antiglob_paths {**/mix/templates/** **/project_layouts/** **/decktemplates/** **/_aside **/_aside/**} *.tm] set count 0 set newdocs [list] set docgen_header_comments "" append docgen_header_comments {[comment {--- punk::docgen generated from inline doctools comments ---}]} \n append docgen_header_comments {[comment {--- punk::docgen DO NOT EDIT DOCS HERE UNLESS YOU REMOVE THESE COMMENT LINES ---}]} \n append docgen_header_comments {[comment {--- punk::docgen overwrites this file ---}]} \n foreach fullpath $matched_paths { puts stdout "do_docgen processing: $fullpath" set doctools [punk::docgen::get_doctools_comments $fullpath] if {$doctools ne ""} { set fname [file tail $fullpath] set mod_tail [file rootname $fname] set relpath [punk::path::relative $codesource_path [file dirname $fullpath]] if {$relpath eq "."} { set relpath "" } set tailsegs [file split $relpath] set module_fullname [join $tailsegs ::]::$mod_tail set target_docname $fname.man set this_outdir [file join $output_base $relpath] if {[string length $fname] > 99} { #output needs to be tarballed to do checksum change tests in a reasonably straightforward and not-too-terribly slow way. #hack - review. Determine exact limit - test if tcllib tar fixed or if it's a limit of the particular tar format #work around tcllib tar filename length limit ( somewhere around 100?) This seems to be a limit on the length of a particular segment in the path.. not whole path length? #this case only came up because docgen used to path munge to long filenames - but left because we know there is a limit and renaming fixes it - even if it's ugly - but still allows doc generation. #review - if we're checking fname - should also test length of whole path and determine limits for tar package require md5 if {[package vsatisfies [package present md5] 2- ] } { set md5opt "-hex" } else { set md5opt "" } set target_docname [md5::md5 {*}$md5opt [encoding convertto utf-8 $fullpath]]_overlongfilename.man puts stderr "WARNING - overlong file name - renaming $fullpath" puts stderr " to [file dirname $fullpath]/$target_docname" } file mkdir $this_outdir puts stdout "saving [string length $doctools] bytes of doctools output from file $relpath/$fname" set outfile [file join $this_outdir $target_docname] set fd [open $outfile w] fconfigure $fd -translation binary puts -nonewline $fd $docgen_header_comments$doctools close $fd incr count lappend newdocs [list $module_fullname $outfile] } } return [list count $count docs $newdocs base $output_base] } } } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ ## Ready package provide punk::mix::commandset::doc [namespace eval punk::mix::commandset::doc { variable pkg punk::mix::commandset::doc variable version set version 999999.0a1.0 }] return