You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							324 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
	
	
							324 lines
						
					
					
						
							14 KiB
						
					
					
				| # -*- tcl -*- | |
| # Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-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 0.1.0 | |
| # Meta platform     tcl | |
| # Meta license      <unspecified> | |
| # @@ 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 punkcheck ;#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 { | |
|             @id         -id ::punk::mix::commandset::doc::validate | |
|             --          -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 0.1.0  | |
| }] | |
| return |