Doc versioning mechanism

The versioning mechanism of the CMake docs is outstanding. I’d like to copy it in a project of mine. I cannot find the sources though, at least not by grepping for “This documents an old version of”
or “Click here to see the latest release” or “select a version from the drop-down menu above”

Is the versioning mechanism part of the CMake sources? Or does it come with a Sphinx module? Is it open source at all?

@brad.king

We configure sphinx with a custom template directory here. That directory contains a custom layout template, which has special version switch code activated by passing -A versionswitch=1 when running sphinx. The version switch code loads version_switch.js, which we publish on the web host here.

The addition of the “This documents an old version of” and “Click here to see the latest release” is done by internal infrastructure we use to prepare the documentation for publication on cmake.org. Basically it updates that same layout.html template:

{%- block relbar1 %}
{{ super() }}
{% if outdated is defined %}
    <div class="outdated">
      This documents an old version of CMake.
      <a href="https://cmake.org/cmake/help/latest/{{ pagename }}.html">
        Click here to see the latest release.
      </a>
      <span class="version_switch_note"></span>
    </div>
{% endif %}
{% endblock %}

Then all the versions except the latest are built with the sphinx -A outdated=1 flag.

2 Likes

That’s very helpful. Many thanks, Brad.

The “internal infrastructure” you mention is just a few scripts to run Sphinx and deploy? Hence nothing I need to worry about before deciding on whether to take the CMake docs as template for my own project?

Yes, the internal infrastructure just makes a few tweaks for publication on cmake.org. It’s nothing heavy. My post above links everything you should need for the version scheme.

Excuse me @brad.king. I wondered how does CMake Team maintain/update version_switch.js and some other important files hosted in the the https://cmake.org/cmake/help automatically?

version_switch.js is updated and pushed to cmake.org manually as part of our release process.

@brad.king

Execuse me. I wondered how is the “Version Switcher” added into the HTML page after using -A versionswitch=1 in the command-line?

The following commands are what I use to build the CMake Docs locally:

sphinx-build --version
git clone --depth 1 --no-single-branch https://github.com/Kitware/CMake.git
cd CMake
git checkout v3.25.1 --quiet
git describe --tag
mkdir build && cd build
cmake ../Utilities/Sphinx -GNinja -DSPHINX_HTML=ON -DSPHINX_FLAGS="-A versionswitch=1"
cmake --build .

And the following is the code snippet of its HTML code:

Compared to the HTML downloaded from cmake.org, we can see that there are lots of stuff inside the <span class="version_switch"></span> label:

How does CMake Team modify this snippet in every HTML file when publishing and hosting on cmake.org?

The included version_switch.js modifies the rendered HTML on-the-fly in the browser memory.
If you save the HTML to disk, you will see the same empty span element as the one you generated.

@jtxa Thansk for you replies!!

It seems that there are some match mechanism in the version_switch.js, checking whether the current href is hosted in the cmake.org/cmake/help. If I want to test this version_switch.js locally, what should I do?

  • The following file is downloaded from the https://cmake.org/cmake/help/latest/ directly:

    version_switch.js (2.4 KB)

  • The following directory is where I generate HTML files:

    D:\Repo\tmp\CMake\build\3.25\html
    
  • The following commands are what I use to generate HTML files:

    git clone --depth 1 --no-single-branch https://github.com/Kitware/CMake.git
    cd CMake
    git checkout v3.25.1 --quiet
    git describe --tag
    mkdir build\3.25 && cd build\3.25
    cmake ../../Utilities/Sphinx -GNinja -DSPHINX_HTML=ON -DSPHINX_FLAGS="-A versionswitch=1"
    cmake --build .
    

Should I modify the version_switch.js to make it suitable for HTML files hosted locally? If so, what should I do?

Problem Description

Hello, CMake Team.

Recently, I checked the contents of https://cmake.org/cmake/help/version_switch.js, and accententally found that there are some changes made by CMake Team.

Because I stored a copy of version_switch.js at the time that the latest release is v3.26 for research purposes, after compareing the previous one (v3.26 is the latest) and the current one (v3.28 is the latest), I found that:

  1. For build_select() function, the current one replaced this line:

    $.each(all_versions, function(version, title) {
    

    with this line:

    Object.entries(all_versions).forEach(([version, title]) => {
    
  2. For on_switch() function, the current one refactored it from:

    Click to expand the previous one
    function on_switch() {
      var selected = $(this).children('option:selected').attr('value');
    
      var url = window.location.href,
          new_url = patch_url(url, selected);
    
      if (new_url != url) {
        // check beforehand if url exists, else redirect to version's start page
        $.ajax({
          url: new_url,
          success: function() {
            window.location.href = new_url;
          },
          error: function() {
            window.location.href = 'https://cmake.org/cmake/help/' + selected;
          }
        });
      }
    }
    

    to the following:

    Click to expand the current one
    function on_switch() {
      const selected = this.options[this.selectedIndex].value;
      const url = window.location.href;
      const new_url = patch_url(url, selected);
    
      if (new_url != url) {
        // check beforehand if url exists, else redirect to version's start page
        fetch(new_url)
          .then((response) => {
            if (response.ok) {
              window.location.href = new_url;
            } else {
              throw new Error(`${response.status} ${response.statusText}`);
            }
          })
          .catch((error) => {
            window.location.href = 'https://cmake.org/cmake/help/' + selected;
          });
      }
    }
    
  3. For $(document).ready(function(), it’s replaced with document.addEventListener totally.

    Click to expand the previous one
    $(document).ready(function() {
      var match = url_re.exec(window.location.href);
      if (match) {
        var release = DOCUMENTATION_OPTIONS.VERSION;
        var version = match[1];
        var select = build_select(version, release);
        $('.version_switch_note').html('Or, select a version from the drop-down menu above.');
        $('.version_switch').html(select);
        $('.version_switch select').bind('change', on_switch);
      }
    });
    
    Click to expand the current one
    document.addEventListener('DOMContentLoaded', () => {
      let match = url_re.exec(window.location.href);
      if (match) {
        const release = DOCUMENTATION_OPTIONS.VERSION;
        const version = match[1];
        const select = build_select(version, release);
        document.querySelectorAll('.version_switch').forEach((placeholder) => {
          placeholder.innerHTML = select;
          let selectElement = placeholder.querySelector('select');
          selectElement.addEventListener('change', on_switch);
        });
        document.querySelectorAll('.version_switch_note').forEach((placeholder) => {
          placeholder.innerHTML = 'Or, select a version from the drop-down menu above.';
        });
      }
    });
    

Out of curiosity, could someone please explain the reason for this change?

May I ask if this is related to that:

  1. the version 3.28 started to use ‘sphinx-build 6.2.1’ to build its documentation,
  2. and cannot use ‘jQuery’ anymore?

Attachments

Yes, the change was because of the update to Sphinx, which no longer includes jQuery in the generated HTML documentation files. The new implementation doesn’t use jQuery and works for all the older versions of the documentation too.

1 Like