JuliaDocs / Documenter.jl

A documentation generator for Julia.
https://documenter.juliadocs.org
MIT License
814 stars 479 forks source link

Document how to generate docs locally #1413

Open aviks opened 4 years ago

aviks commented 4 years ago

Apologies if I've missed something, but I could not find, either by reading the Documenter docs, or googling, how to generate the docs locally while developing. The correct incantation is present in most travis configs, and was pointed out to me on Slack. Unless I have missed something obvious, I think it would be good to document the commands needed. They are not obvious, at least to me. At the very least, this issue can serve as a reminder to myself, since I had to look it up multiple times over the last months as I developed new packages.

This is what I ended up doing, running from a shell. This presumes that the current directory is the package root, and that there exists a docs specific Project.toml. This project file must contain Documenter and the package in question as depdendencies. The following incantations can add the depedencies required, and generate the manifest

julia --project=docs/ -e 'using Pkg; Pkg.add("Documenter")'
julia --project=docs/ -e 'using Pkg; Pkg.add("MyPkg")'
julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd()));Pkg.instantiate()'

After that, the following incantation runs the doc generation.

julia --project=docs/  docs/make.jl
mortenpi commented 4 years ago

Yeah, this would be good to have in the manual somewhere. What I usually do is the following, if you have the standard setup with docs/Project.toml:

$ julia --project=docs/
pkg> instantiate
pkg> dev .
julia> include("docs/make.jl")

You can even drop a using Revise in there so that you wouldn't have to reload Julia when updating the docstrings.

aviks commented 4 years ago

What I usually do is the following

Yeah, that sounds reasonable. All of this is, of course, obvious in retrospect, but I've struggle with figuring this out, and seen other's do that.

haampie commented 3 years ago

This came up on Slack too; the Documenter docs are making people run julia make.jl all the time.

haampie commented 3 years ago

It would be nice to write a guide targeted for package developers who interactively write docs and view the result in their browser. I wrote a bit about how to do it from scratch, how to use Revise etc, but unfortunately Revise does not pick up docs for new function definitions it seems :( so I'm not sure if a Revise-first approach is that great

MichaelHatherly commented 3 years ago

unfortunately Revise does not pick up docs for new function definitions it seems :( so I'm not sure if a Revise-first approach is that great

Docstring updates work for me on Revise 3.1.4, with either tracked packages of just using includet. (Everything should be Revise-first :smiley:)

It would be nice to write a guide targeted for package developers who interactively write docs and view the result in their browser

We're happy to accept PRs for the docs, there's always improvements that could be made.

haampie commented 3 years ago

You are absolutely right :laughing:. It was late yesterday, I forgot to either export the function from the package or reference it properly in the docs :D. I'll make that PR later tonight then.

haampie commented 3 years ago

Hm, still experiencing issues when using Revise. I was writing the following for an interactive package development / docs writing guide, but if you follow along and get to the last step... it doesn't pick up the new function with its docs. Can someone reproduce?


Guide for package developers

This guide is intended for users who write docs as they develop a new package. For this use case you want to keep a single Julia REPL session open throughout the guide. To keep track of code and docs changes of your package, you need to install Revise.jl globally first. Enter the Pkg REPL by pressing ] and run:

(@v1.5) pkg> add Revise

julia> using Revise

Go back to the Pkg REPL and generate a new package:

(@v1.5) pkg> generate Example
 Generating  project Example:
    Example/Project.toml
    Example/src/Example.jl

Use your favorite editor to create a docs/ folder within the package such that you end up with the following structure (make.jl and index.md empty files):

Example/
├── docs
│   ├── make.jl
│   └── src
│       └── index.md
├── Project.toml
└── src
    └── Example.jl

Typically Documenter is not a true dependency of your package, but merely a development tool. Therefore, create a fresh environment in the docs/ folder and add Documenter and the Example package itself as dependencies:

(@v1.5) pkg> activate Example/docs
 Activating new environment at `~/Documents/projects/Example/docs/Project.toml`

(docs) pkg> add Documenter

(docs) pkg> dev Example
[ Info: Resolving package identifier `Example` as a directory at `~/Documents/projects/Example`.

Open make.jl in your editor and fill it with the following minimal boilerplate:

using Documenter, Example

makedocs(sitename="My Documentation")

You might also want to add contents to index.md:

# Example.jl Documentation

Hello world!

In your REPL you can now include the make.jl script:

julia> cd("Example")

julia> include("docs/make.jl")
[ Info: Precompiling Example [86743d7e-c321-4af5-beac-c79b42746a03]
[ Info: SetupBuildDirectory: setting up build directory.
[ Info: Doctest: running doctests.
[ Info: ExpandTemplates: expanding markdown templates.
[ Info: CrossReferences: building cross-references.
[ Info: CheckDocument: running document checks.
[ Info: Populate: populating indices.
[ Info: RenderDocument: rendering document.
[ Info: HTMLWriter: rendering HTML pages.

The docs/ folder now contains the file build/index.html. Open this in your browser to view the generated docs.

Next, we add a new function to our package and document it. Replace the contents of Example.jl with the following:

module Example

export func

"""
    func(x)

Returns double the number `x` plus `1`.
"""
func(x) = 2x + 1

end

And document this function in index.md using

# Example.jl Documentation

```@docs
func(x)

In the REPL we can now include the `make.jl` file once more:

```julia
julia> include("docs/make.jl")
[ Info: SetupBuildDirectory: setting up build directory.
[ Info: Doctest: running doctests.
[ Info: ExpandTemplates: expanding markdown templates.
┌ Warning: no docs found for 'func(x)' in `@docs` block in src/index.md:3-5
│ ```@docs
│ func(x)
│ ```
└ @ Documenter.Expanders ~/.julia/packages/Documenter/pjwqp/src/Expanders.jl:334
[ Info: CrossReferences: building cross-references.
[ Info: CheckDocument: running document checks.
[ Info: Populate: populating indices.
[ Info: RenderDocument: rendering document.
[ Info: HTMLWriter: rendering HTML pages.

Now refresh the index.html page in your browser

fredrikekre commented 3 years ago

I think you need

```@docs
Example.func(x)
or

makedocs(..., modules=[Example])


?
haampie commented 3 years ago

With both those changes it doesn't seems to work for me:

┌ Warning: no docs found for 'Example.func(x)' in `@docs` block in src/index.md:3-5
│ ```@docs
│ Example.func(x)
│ ```
└ @ Documenter.Expanders ~/.julia/packages/Documenter/pjwqp/src/Expanders.jl:334

Let me try once more from the start... edit: nope. Revise doesn't seem to pick it up for me. That's a bit of a bummer, since it's hard to write a guide that advocates interactive development & viewing docs if this doesn't work out of the box.

fredrikekre commented 3 years ago

I used this pattern all day yesterday and it worked flawlessly...

haampie commented 3 years ago

Hm. And I'm pretty sure I used this approach succesfully 3 years ago too. I wonder what's different here.

fredrikekre commented 3 years ago

Do you have Revise v3?

haampie commented 3 years ago
(@v1.5) pkg> st Revise
Status `~/.julia/environments/v1.5/Project.toml`
  [295af30f] Revise v3.1.5
haampie commented 3 years ago

After restarting the REPL it does pick up changes by the way :). Let's see if we can avoid having to put that step in the guide.

fredrikekre commented 3 years ago

Ah, I know what is wrong. Revise does not track packages recursively into include, so you need

using Revise, Example
include("docs/make.jl")
haampie commented 3 years ago

Oh, thanks for pointing that out. Only more reasons to document this workflow!

Edit: unfortunately not working (yet) when explicitly using Example after using Revise and before the first include("docs/make.jl"). Will try again later.

haampie commented 3 years ago

Seems like the problem is no new docstrings are picked up if the package had no docstrings in the first place.

Nauss commented 3 years ago

I tried to use this approach to generate the docs locally on my Windows 10 machine (julia 1.5.2). I already had an existing project so I skipped the generate step but I run:

(docs) pkg> dev MyProject

I get the following error:

  Updating registry at `C:\Users\...\.julia\registries\General`
ERROR: The following package names could not be resolved:
 * MyProject (36230cab-47f6-4611-a9ad-d42316f9a74f in manifest but not in project)

If I use dev ../MyProject instead, everything else works the same.

Should this be mentioned as an alternative or am I missing something ?

mortenpi commented 3 years ago

@Nauss pkg> dev is relative to the current working directory and the example above assumes you're in the parent directory of MyProject. In your case, as you're already in MyProject/, you could just run pkg> dev ..

Nauss commented 3 years ago

Thanks !!

I'm pretty new to Julia and knew I was missing something !

goerz commented 8 months ago

How about adding the following code at the top of default make.jl file that DocumenterTools.generate produces:

if abspath(PROGRAM_FILE) == @__FILE__
    # When running the `make.jl` file as a script, automatically activate the
    # `docs` environment and dev-install the main package into that environment
    import Pkg
    Pkg.activate(@__DIR__)
    Pkg.develop(path=joinpath(@__DIR__, ".."))
    Pkg.instantiate()
end

That code snippet could be discussed in more detail in a rewrite of the Documenter Guide.

It would probably help out a lot of people, and with the if abspath(PROGRAM_FILE) == @__FILE__ it wouldn't get in the way of alternative workflows. For example, my own workflow is that I instantiate the test environment and include(docs/make.jl) there. This is because I prefer a single REPL where I can interactively run tests, build documentation, or whatever else I might need while developing the package.

mortenpi commented 8 months ago

I think my preference would be to have it as a separate script (but one that we could definitely auto-generate for the user). I'd prefer not to re-run all the Pkg machinery every time I do julia --project=docs/ docs/make.jl.