private void catPublish()

in grails-gradle/docs-core/src/main/groovy/grails/doc/DocPublisher.groovy [153:428]


    private void catPublish() {
        initialize()
        if (!src?.exists()) {
            return
        }

        // unpack documentation resources
        String docResources = "${workDir}/doc-resources"
        ant.mkdir(dir: docResources)
        unpack(dest: docResources, src: "grails-doc-files.jar")

        def refDocsDir = calculateLanguageDir(target?.absolutePath ?: "./docs")
        def refGuideDir = new File(refDocsDir, "guide")
        def refPagesDir = "$refGuideDir/pages"

        ant.mkdir(dir: refDocsDir)
        ant.mkdir(dir: refGuideDir)
        ant.mkdir(dir: refPagesDir)
        ant.mkdir(dir: "$refDocsDir/ref")

        String imgsDir = new File(refDocsDir, calculatePathToResources("img")).path
        File fontsDir = new File(refDocsDir, calculatePathToResources("fonts"))
        ant.mkdir(dir: imgsDir)
        ant.mkdir(dir: fontsDir )
        String cssDir = new File(refDocsDir, calculatePathToResources("css")).path
        ant.mkdir(dir: cssDir)
        String jsDir = new File(refDocsDir, calculatePathToResources("js")).path
        ant.mkdir(dir: jsDir)
        ant.mkdir(dir: "${refDocsDir}/ref")

        ant.copy(todir: imgsDir, overwrite: true) {
            fileset(dir: "${docResources}/img")
        }

        if (images && images.exists()) {
            ant.copy(todir: imgsDir, overwrite: true, failonerror:false) {
                fileset(dir: images)
            }
        }

        ant.copy(todir: cssDir, overwrite: true) {
            fileset(dir: "${docResources}/css")
        }
        ant.copy(todir: fontsDir, overwrite: true) {
            fileset(dir: "${docResources}/fonts")
        }

        if (css && css.exists()) {
            ant.copy(todir: cssDir, overwrite: true, failonerror:false) {
                fileset(dir: css)
            }
        }
        if (fonts && fonts.exists()) {
            ant.copy(todir: fontsDir, overwrite: true, failonerror:false) {
                fileset(dir: fonts)
            }
        }
        ant.copy(todir: jsDir, overwrite: true) {
            fileset(dir: "${docResources}/js")
        }
        if (js && js.exists()) {
            ant.copy(todir: jsDir, overwrite: true, failonerror:false) {
                fileset(dir: js)
            }
        }
        if (style && style.exists()) {
            ant.copy(todir: "${docResources}/style", overwrite: true, failonerror:false) {
                fileset(dir: style)
            }
        }

        // Build the table of contents as a tree of nodes. We currently support
        // two strategies for this:
        //
        //  1. From a toc.yml file
        //  2. From the gdoc filenames
        //
        // The first strategy is used if the TOC file exists, otherwise we call
        // back to the old way of doing it, which means putting the section
        // numbers in the gdoc filenames.
        def guideSrcDir = new File(src, "guide")
        def yamlTocFile = new File(guideSrcDir, TOC_FILENAME)
        def guide
        def ext = asciidoc ? ".adoc" : ".gdoc"

        if (yamlTocFile.exists()) {
            def tocStrategy = new YamlTocStrategy(new FileResourceChecker(guideSrcDir), ext)
            guide = tocStrategy.generateToc(yamlTocFile)

            // A set of all gdoc files.
            def files = []
            def pattern = asciidoc ? ~/^.+\.adoc$/ : ~/^.+\.gdoc$/
            guideSrcDir.traverse(type: FileType.FILES, nameFilter: pattern) {
                // We need relative file paths with '/' separators, since those
                // are what are stored in the UserGuideNodes.
                files << (it.absolutePath - guideSrcDir.absolutePath)[1..-1].
                        replace(File.separator as char, '/' as char)
            }

            def errors = verifyToc(guideSrcDir, files, guide)
            if (errors) {
                throw new RuntimeException("Encountered the following errors while building table of contents. Aborting.\n${errors.join("\n")}")
            }

            for (ch in guide.children) {
                overrideAliasesFromToc(ch)
            }
        }
        else {
            throw new RuntimeException("Legacy TOC is no longer supported. Please add a ${TOC_FILENAME}")
        }

        // When migrating from the old style docs to the new style, existing
        // external links that use URL fragment identifiers will break. To
        // mitigate against this problem, the user can provide a list of mappings
        // from the new fragment identifiers to the old ones. The docs will then
        // include both.
        def legacyLinksFile = new File(guideSrcDir, "links.yml")
        def legacyLinks = [:]
        if (legacyLinksFile.exists()) {
            legacyLinksFile.withInputStream { input ->
                legacyLinks = new Yaml(new SafeConstructor(new LoaderOptions())).load(input)
            }
        }

        def templateEngine = new groovy.text.SimpleTemplateEngine()

        // Reference menu items.
        def sectionFilter = { it.directory && !it.name.startsWith('.') } as FileFilter
        def files = new File(src, "ref").listFiles(sectionFilter)?.toList()?.sort() ?: []
        def refCategories = files.collect { f ->
            new Expando(
                    name: f.name,
                    usage: new File("${src}/ref/${f.name}$ext"),
                    sections: f.listFiles().findAll { it.name.endsWith(ext) }.sort())
        }

        def fullToc = new StringBuilder()

        def pathToRoot = ".."
        Map vars = new LinkedHashMap(engineProperties)
        vars.putAll(
            encoding: encoding,
            title: title,
            docTitle: title,
            subtitle: subtitle,
            footer: footer, // TODO - add a way to specify footer
            authors: authors,
            translators: translators,
            version: version,
            refMenu: refCategories,
            toc: guide,
            copyright: copyright,
            logo: injectPath(logo, pathToRoot),
            sponsorLogo: injectPath(sponsorLogo, pathToRoot),
            single: false,
            path: pathToRoot,
            resourcesPath: calculatePathToResources(pathToRoot),
            prev: null,
            next: null,
            legacyLinks: legacyLinks,
            sourceRepo: sourceRepo,
        )

        if(engine instanceof AsciiDocEngine) {
            // pass attributes to asciidoc
            ((AsciiDocEngine)engine).attributes.putAll(
                    version: version,
                    apiDocs: "https://docs.grails.org/${version}/api/",
                    sourceRepo: sourceRepo
            )
            ((AsciiDocEngine)engine).attributes.putAll(
                    engineProperties
            )
        }


        // Build the user guide sections first.
        def template = templateEngine.createTemplate(new File("${docResources}/style/guideItem.html").newReader(encoding))
        def sectionTemplate = templateEngine.createTemplate(new File("${docResources}/style/section.html").newReader(encoding))
        def fullContents = new StringBuilder()

        def chapterVars
        def chapters = guide.children
        chapters.eachWithIndex{ chapter, i ->
            chapterVars = [*:vars, chapterNumber: i + 1]
            if (i != 0) {
                chapterVars['prev'] = chapters[i - 1]
            }
            if (i != (chapters.size() - 1)) {
                chapterVars['next'] = chapters[i + 1]
            }
            chapterVars.sectionNumber = (i + 1).toString()
            writeChapter(chapter, template, sectionTemplate, guideSrcDir, refGuideDir.path, fullContents, chapterVars)
        }

        files = new File("${src}/ref").listFiles()?.toList()?.sort() ?: []
        def reference = [:]
        template = templateEngine.createTemplate(new File("${docResources}/style/referenceItem.html").newReader(encoding))

        pathToRoot = "../.."
        vars.logo = injectPath(logo, pathToRoot)
        vars.sponsorLogo = injectPath(sponsorLogo, pathToRoot)
        vars.path = pathToRoot
        vars.resourcesPath = calculatePathToResources(pathToRoot)

        // Generate the reference section of the guide.
        for (f in files) {
            if (f.directory && !f.name.startsWith(".")) {
                def section = f.name
                vars.section = section

                new File("${refDocsDir}/ref/${section}").mkdirs()
                def textiles = f.listFiles().findAll { it.name.endsWith(ext)}.sort()
                def usageFile = new File("${src}/ref/${section}${ext}")
                if (usageFile.exists()) {
                    def data = usageFile.getText("UTF-8")
                    context.set(DocEngine.SOURCE_FILE, usageFile)
                    context.set(DocEngine.CONTEXT_PATH, pathToRoot)
                    context.set(DocEngine.API_CONTEXT_PATH, vars.resourcesPath)
                    output.warn "Rendering document file $usageFile.name"
                    vars.content = engine.render(data, context)
                    vars.sourcePath = "ref/$usageFile.name"
                    new File("${refDocsDir}/ref/${section}/Usage.html").withWriter(encoding) {out ->
                        template.make(vars).writeTo(out)
                    }
                }
                for (txt in textiles) {
                    def name = txt.name[0..-6]
                    def data = txt.getText("UTF-8")
                    context.set(DocEngine.SOURCE_FILE, txt.name)
                    context.set(DocEngine.CONTEXT_PATH, pathToRoot)
                    context.set(DocEngine.API_CONTEXT_PATH, vars.resourcesPath)
                    output.warn "Rendering document file $txt.name"
                    vars.content = engine.render(data, context)
                    vars.sourcePath = "ref/${section}/$txt.name"
                    new File("${refDocsDir}/ref/${section}/${name}.html").withWriter(encoding) {out ->
                        template.make(vars).writeTo(out)
                    }
                }
            }
        }

        vars.remove("section")
        vars.content = fullContents.toString()
        vars.single = true

        pathToRoot = ".."
        vars.logo = injectPath(logo, pathToRoot)
        vars.sponsorLogo = injectPath(sponsorLogo, pathToRoot)
        vars.path = pathToRoot
        vars.resourcesPath = calculatePathToResources(pathToRoot)

        template = templateEngine.createTemplate(new File("${docResources}/style/layout.html").newReader(encoding))
        new File("${refGuideDir}/single.html").withWriter(encoding) {out ->
            template.make(vars).writeTo(out)
        }

        vars.content = ""
        vars.single = false
        new File("${refGuideDir}/index.html").withWriter(encoding) {out ->
            template.make(vars).writeTo(out)
        }

        pathToRoot = "."
        vars.logo = injectPath(logo, pathToRoot)
        vars.sponsorLogo = injectPath(sponsorLogo, pathToRoot)
        vars.path = pathToRoot
        vars.resourcesPath = calculatePathToResources(pathToRoot)

        new File("${refDocsDir}/index.html").withWriter(encoding) {out ->
            template.make(vars).writeTo(out)
        }

        ant.echo "Built user manual at ${refDocsDir}/index.html"
    }