From cfe4a20cb84bb5575f4c5f0cfa42e9b5463238ea Mon Sep 17 00:00:00 2001 From: Florian Sattler Date: Sun, 6 Feb 2022 23:27:48 +0100 Subject: [PATCH] Adapt folder layout and finish pandoc integration (#69) Completes the necessary folder layout rework and refactors pandoc file generation to work together with the rest of the tooling/setup. This finally finishes the pandoc integration to automatically generate teaching docs. --- .github/workflows/ci.yml | 2 +- .github/workflows/ci_tool_tests.yml | 31 ++ .github/workflows/release.yml | 62 +--- .gitignore | 12 + .mailmap | 1 + README.md | 5 +- TODO.txt | 7 - config/spellcheck/ignored_words.txt | 5 +- tools/old/skeleton.md => skeleton.md | 61 ++-- sources/Makefile | 45 ++- sources/course_examples.md | 5 - sources/disclaimer.md | 6 - sources/index.html | 5 +- sources/introduction.md | 13 +- sources/{main.md => main_raw.md} | 20 +- .../compile-time-programming/concepts.md | 5 +- .../function-templates.md | 5 +- .../requires-clause.md | 2 +- .../requires-expressions.md | 31 +- .../modules/functions/calling-functions.md | 2 +- .../modules/functions/defaulted-parameters.md | 34 +- sources/modules/functions/member-functions.md | 2 +- .../functions/user-defined-literals.md | 2 +- .../meta-error-handling/static_assert.md | 2 +- .../modules/object-model/constant-objects.md | 2 +- sources/modules/object-model/constructors.md | 2 +- .../modules/object-model/copy-semantics.md | 2 +- sources/modules/object-model/declarations.md | 2 +- .../modules/object-model/move-semantics.md | 2 +- sources/modules/object-model/objects.md | 2 +- sources/modules/object-model/rule-of-five.md | 2 +- sources/modules/object-model/rule-of-zero.md | 2 +- .../object-model/special-member-functions.md | 2 +- sources/modules/object-model/types.md | 2 +- sources/obtaining_document.md | 19 +- tools/build/aspell_frontend | 12 +- tools/build/build | 87 ++--- tools/build/deploy | 312 ------------------ tools/build/generate_main.py | 78 +++++ tools/build/make_markdown | 38 +-- tools/build/preprocessor | 1 - tools/mgmt_tools/topic_updater.py | 20 +- tools/old/Readme.md | 50 --- tools/pytest.ini | 2 +- tools/requirements.txt | 3 +- tools/tests/TEST_INPUTS/fix_wrong_sections.md | 12 +- tools/tests/TEST_INPUTS/missing_sections.md | 8 +- .../tests/TEST_INPUTS/test_injectable_main.md | 1 + .../test_modules/test_empty_module.md | 84 +++++ .../test_modules/test_placeholder.md | 6 + .../test_modules/test_wrong_placeholder.md | 6 + tools/tests/TEST_INPUTS/test_skeleton.md | 12 +- .../TEST_INPUTS/user_content_heading_topic.md | 12 +- tools/tests/generate_main_test.py | 59 ++++ tools/tests/topic_updater_test.py | 63 ++-- 55 files changed, 573 insertions(+), 697 deletions(-) create mode 100644 .github/workflows/ci_tool_tests.yml create mode 100644 .mailmap delete mode 100644 TODO.txt rename tools/old/skeleton.md => skeleton.md (60%) delete mode 100644 sources/course_examples.md delete mode 100644 sources/disclaimer.md rename sources/{main.md => main_raw.md} (69%) delete mode 100755 tools/build/deploy create mode 100755 tools/build/generate_main.py delete mode 100644 tools/old/Readme.md create mode 100644 tools/tests/TEST_INPUTS/test_injectable_main.md create mode 100644 tools/tests/TEST_INPUTS/test_modules/test_empty_module.md create mode 100644 tools/tests/TEST_INPUTS/test_modules/test_placeholder.md create mode 100644 tools/tests/TEST_INPUTS/test_modules/test_wrong_placeholder.md create mode 100644 tools/tests/generate_main_test.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41476a3..81af683 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,6 @@ jobs: run: | tools/build/build \ -d ${{runner.temp}}/output \ - -z ${{github.ref}} \ + -v ${GITHUB_REF#refs/*/} \ -s ############################################################ diff --git a/.github/workflows/ci_tool_tests.yml b/.github/workflows/ci_tool_tests.yml new file mode 100644 index 0000000..ac35f70 --- /dev/null +++ b/.github/workflows/ci_tool_tests.yml @@ -0,0 +1,31 @@ +# This action is used to run tests to ensure all tools work like expected. + +name: Tool CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + pip install -r tools/requirements.txt + + - name: Run unittests + run: | + cd tools + pytest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3db1cda..8347adc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,60 +20,20 @@ jobs: shell: bash run: tools/build/prebuild ############################################################ + - name: Collect git metadata + id: git_metadata + run: | + echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} # The following builds the document in multiple formats for deployment. - name: Build the document. shell: bash run: | tools/build/build \ -d ${{runner.temp}}/output \ - -z ${{github.ref}} - ############################################################ - # The following deploys to a different branch of the same repository - # using an access token for authentication. - # Note: It is recommended that this approach not be used. - #- name: Deploy the built document to a GitHub Pages branch. - # env: - # DEPLOY_TOKEN: ${{secrets.DEPLOY_TOKEN}} - # shell: bash - # run: | - # tools/build/deploy \ - # -f \ - # -m token \ - # -t ${{runner.temp}}/deploy_tmp \ - # -i ${{runner.temp}}/output \ - # -r mdadams/SG20 \ - # -b gh-pages \ - # -z ${{github.ref}} - ############################################################ - # The following deploys to a different branch of the same repository - # using a deploy (i.e., SSH) key for authentication. - - name: Deploy the built document to a GitHub Pages branch. - env: - DEPLOY_KEY: ${{secrets.DEPLOY_KEY}} - shell: bash - run: | - tools/build/deploy \ - -f \ - -m key \ - -t ${{runner.temp}}/deploy_tmp \ - -i ${{runner.temp}}/output \ - -r mdadams/SG20 \ - -b gh-pages \ - -z ${{github.ref}} - ############################################################ - # The following deploys to a branch of a different repository - # using a deploy (i.e., SSH) key authentication. - - name: Deploy the built document to a GitHub Pages branch. - env: - DEPLOY_KEY: ${{secrets.ALT_DEPLOY_KEY}} - shell: bash - run: | - tools/build/deploy \ - -f \ - -m key \ - -t ${{runner.temp}}/deploy_tmp_2 \ - -i ${{runner.temp}}/output \ - -r mdadams/sg20_guidelines_for_teaching_cpp \ - -b gh-pages \ - -z ${{github.ref}} - ############################################################ + -v ${{ steps.git_metadata.outputs.VERSION }} + - name: Deploy generated content to gh-pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ${{runner.temp}}/output + keep_files: true diff --git a/.gitignore b/.gitignore index c18dd8d..d265188 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,13 @@ __pycache__/ + +# Ignore generated files +sources/contributors.md +sources/guidelines.epub +sources/guidelines.html +sources/guidelines.texi +sources/guidelines_html/ +sources/knowledge_areas_summary.md +sources/main.gen.md +sources/main.pre.md +sources/spellcheck_expected_sorted.txt +sources/spellcheck_result.txt diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..603128f --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Florian Sattler diff --git a/README.md b/README.md index e8799f5..a005c17 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,7 @@ This repository contains the source for the document: When the repository is tagged, this document is automatically built and made available via GitHub Pages at: - - (soon) - - - (currently) - - (currently) + - # Prerequisites for Building the Document diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 58e34e9..0000000 --- a/TODO.txt +++ /dev/null @@ -1,7 +0,0 @@ -Some topics listed on GitHub are unclear as to their meaning. -e.g. Heterogeneous Compiling -[Is "Heterogeneous Computing" intended or "Cross Compiling"?] -There are others too. - -More thought is needed on naming conventions for labels for -knowledge areas/units. diff --git a/config/spellcheck/ignored_words.txt b/config/spellcheck/ignored_words.txt index 1694cd3..40d6c40 100644 --- a/config/spellcheck/ignored_words.txt +++ b/config/spellcheck/ignored_words.txt @@ -10,6 +10,8 @@ expr Florian func Furst +Hinnant +html Hyland JC Krathwohl @@ -21,7 +23,6 @@ Sattler SG Stroustrup udl +Vandevoorde ver Winkel -Hinnant -Vandevoorde diff --git a/tools/old/skeleton.md b/skeleton.md similarity index 60% rename from tools/old/skeleton.md rename to skeleton.md index 10a99cb..2cc384e 100644 --- a/tools/old/skeleton.md +++ b/skeleton.md @@ -1,48 +1,41 @@ -# Module name: topic name +## Module name: topic name + _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ -## Overview +### Overview _Provides a short natural language abstract of the module’s contents._ _Specifies the different levels of teaching._ - - - - - - - - - - - - - - - - - -
LevelObjectives
Foundational
Main
Advanced
- -## Motivation +------------------------------------------------------------------------ +Level Objective +----------------- ------------------------------------------------------ +Foundational --- + +Main --- + +Advanced --- + +------------------------------------------------------------------------ + +### Motivation _Why is this important?_ _Why do we want to learn/teach this topic?_ -## Topic introduction +### Topic introduction _Very brief introduction to the topic._ -## Foundational: Using * +### Foundational: Using * -### Background/Required Knowledge +#### Background/Required Knowledge A student: -### Student outcomes +#### Student outcomes _A list of things "a student should be able to" after the curriculum._ _The next word should be an action word and testable in an exam._ @@ -56,22 +49,22 @@ A student should be able to: 4. 5. -### Caveats +#### Caveats _This section mentions subtle points to understand, like anything resulting in implementation-defined, unspecified, or undefined behavior._ -### Points to cover +#### Points to cover _This section lists important details for each point._ -## Main: implementing * +### Main: implementing * -### Background/Required Knowledge +#### Background/Required Knowledge * All of the above. -### Student outcomes +#### Student outcomes A student should be able to: @@ -81,11 +74,11 @@ A student should be able to: 4. 5. -### Caveats +#### Caveats -### Points to cover +#### Points to cover -## Advanced +### Advanced _These are important topics that are not expected to be covered but provide guidance where one can continue to investigate this topic in more depth._ diff --git a/sources/Makefile b/sources/Makefile index b3a5121..40b59ea 100644 --- a/sources/Makefile +++ b/sources/Makefile @@ -15,26 +15,26 @@ DOC_SPELLCHECK_MUST_PASS = 1 INSTALL_DIR = $(TOP_DIR)/install # All of the Markdown source files (that are not generated during build). -SOURCES = \ - modules/compile-time-programming/requires-expressions.md \ - modules/functions/defaulted-parameters.md \ - modules/functions/user-defined-literals.md \ - modules/object-model/copy-semantics.md \ - course_examples.md \ - disclaimer.md \ - glossary.md \ +SOURCES = $(shell find . -mindepth 2 -name '*.md') + +# Special top-level markdown files +EXTRA_SOURCES = glossary.md \ contributing.md \ introduction.md \ - main.md \ + main_raw.md \ obtaining_document.md \ references.md \ # The Markdown files that are generated during the build process. GENERATED_MARKDOWN = \ knowledge_areas_summary.md \ + main.pre.md \ main.gen.md \ contributors.md \ +# Merge extra sources with detected sources for teaching modules +SOURCES += $(EXTRA_SOURCES) + ################################################################################ # Define primary targets. ################################################################################ @@ -96,6 +96,7 @@ install: all # Some additional configuration. ################################################################################ +MAIN_GENERATOR= $(TOP_DIR)/tools/build/generate_main.py MD_PREPROCESSOR = $(TOP_DIR)/tools/build/preprocessor MAKE_MARKDOWN = $(TOP_DIR)/tools/build/make_markdown SPELLCHECK_DIR = $(TOP_DIR)/config/spellcheck @@ -104,14 +105,17 @@ SPELLCHECK_DIR = $(TOP_DIR)/config/spellcheck # Preprocessing setup. ################################################################################ -main.gen.md: $(SOURCES) contributors.md main.md - $(MD_PREPROCESSOR) -v $(DOC_VERSION) < main.md > main.gen.md +main.pre.md: $(SOURCES) contributors.md main_raw.md + $(MAIN_GENERATOR) --raw main_raw.md --out main.pre.md --module-folder modules + +main.gen.md: $(SOURCES) contributors.md main.pre.md + $(MD_PREPROCESSOR) -v $(DOC_VERSION) < main.pre.md > main.gen.md knowledge_areas_summary.md: $(SOURCES) knowledge_areas.dat $(MAKE_MARKDOWN) < knowledge_areas.dat > knowledge_areas_summary.md contributors.md: - git log --all --pretty="%an" | sort | uniq > contributors.md + git log --all --pretty="%aN" | sort | uniq > contributors.md ################################################################################ # Establish Pandoc settings. @@ -163,21 +167,16 @@ guidelines.tex: spellcheck_result.txt: guidelines.html rm -f $@ - rm -f spellcheck_expected_sorted.txt - sort $(SPELLCHECK_DIR)/ignored_words.txt | \ - uniq > spellcheck_expected_sorted.txt PATH="$(TOP_DIR)/tools/build:$$PATH" pandoc --from $(INPUT_FORMAT) \ --lua-filter $(TOP_DIR)/tools/pandoc_filters/spellcheck.lua \ main.gen.md | sort | uniq > $@ - @status=0; \ - diff -q spellcheck_expected_sorted.txt $@ || \ - status=1; \ - if [ $$status -ne 0 ]; then \ - echo "SPELLING ERRORS DETECTED:"; \ - diff -u spellcheck_expected_sorted.txt $@ | \ - grep '^[+-]'; \ + if [ -s $@ ]; then \ + echo "DETECTED SPELLING ERRORS:"; \ + cat $@ | while read line; do echo "Misspelled '$$line' in:"; \ + grep -R "$$line" -n --color=auto --include='*.md' --exclude='main.gen.md'; done; \ + sync; \ if [ $(DOC_SPELLCHECK_MUST_PASS) -ne 0 ]; then \ - echo "ERROR: spelling errors detected"; \ + echo "ERROR: spelling errors detected, cannot proceed!"; \ exit 1; \ else \ echo "WARNING: spelling errors detected"; \ diff --git a/sources/course_examples.md b/sources/course_examples.md deleted file mode 100644 index 2d078cc..0000000 --- a/sources/course_examples.md +++ /dev/null @@ -1,5 +0,0 @@ -# Examples of Course Curricula - -[**NOTE**: Anyone have any suggestions of items to add here?] -The following are examples of curricula for course on C++: -... diff --git a/sources/disclaimer.md b/sources/disclaimer.md deleted file mode 100644 index c2be6f6..0000000 --- a/sources/disclaimer.md +++ /dev/null @@ -1,6 +0,0 @@ -# Disclaimer - -This document is intended as a proof of concept to solicit feedback -from others. -This document is incomplete. -This document likely has at least a few errors. diff --git a/sources/index.html b/sources/index.html index 604b236..6bf9c53 100644 --- a/sources/index.html +++ b/sources/index.html @@ -1,6 +1,7 @@ - + + -

If you are not redirected in five seconds, click here.

+

If you are not redirected in five seconds, click here.

diff --git a/sources/introduction.md b/sources/introduction.md index 196ef89..b910b25 100644 --- a/sources/introduction.md +++ b/sources/introduction.md @@ -46,10 +46,6 @@ please read the section [How to Contribute](#contributing). The various concepts (i.e., ideas) to potentially be covered are partitioned into modules. A module is very broad in scope and consists of numerous topics. -**[Note: Can I suggest that we use the term "area" or "unit" instead of -"module"? I think that these other choices are better and also -avoid any potential confusion over what is meant by "module" -(i.e., C++ term versus plain English term).]** For each module, topics related to the module are identified. Then, for each topic, learning outcomes are specified. @@ -61,21 +57,18 @@ This allows target audiences with different background and learning objectives to be accommodated. The three proficiency levels are as follows: - - foundational. This level gives the learner the idea that a + - foundational: This level gives the learner the idea that a facility exists, what benefits it offers, and the basic ways of using it. - **[Note: Isn't this just "novice"/"beginner"?]** - - main. This level shows mainstream uses and techniques. + - main: This level shows mainstream uses and techniques. For abstraction and organizational mechanisms it also demonstrates how to build them. This level should also give the learner a basic (but not detailed) understanding of how a facility might be implemented so that the learner can have a first-order understanding of any costs involved. - **[Note: The term "main" is not very descriptive/helpful. Could I suggest - using "intermediate"?]** - - advanced. This level gives information suitable for an expert. + - advanced: This level gives information suitable for an expert. For most topics there is an expert level of knowledge that most programmers rarely need and techniques that require detailed understanding of language rules or library implementation. diff --git a/sources/main.md b/sources/main_raw.md similarity index 69% rename from sources/main.md rename to sources/main_raw.md index 71e462d..d847a5e 100644 --- a/sources/main.md +++ b/sources/main_raw.md @@ -8,8 +8,6 @@ output: pdf_document __INCLUDE__(obtaining_document.md) -__INCLUDE__(disclaimer.md) - __INCLUDE__(introduction.md) # Summary of Modules and Topics @@ -30,32 +28,16 @@ this is indicated by an em dash ("—"). In the case that the information for a topic is completely missing, a question mark ("?") symbol is used. -[**NOTE**: These topics are taken mostly from the SG20 GitHub repository. -They are not intended to be complete in any sense. -In fact, by gathering together all topics in one place where they are -easily viewed, it is hoped that missing and unbalanced items will be more -obvious.] - __INCLUDE__(knowledge_areas_summary.md) # Detailed Information for Modules and Topics [//]: # ( ********** START OF DETAILED TOPIC DOCUMENTS ********** ) -__INCLUDE__(modules/object-model/copy-semantics.md) - -__INCLUDE__(modules/functions/user-defined-literals.md) - -__INCLUDE__(modules/functions/defaulted-parameters.md) - -__INCLUDE__(modules/compile-time-programming/requires-expressions.md) - -__INCLUDE__(modules/meta-error-handling/static_assert.md) +INJECT_TEACHING_MODULES_HERE [//]: # ( ********** END OF DETAILED TOPIC DOCUMENTS ********** ) -__INCLUDE__(course_examples.md) - # License {#license} **[NOTE: This license is copied verbatim from the C++ Core Guidelines.]** diff --git a/sources/modules/compile-time-programming/concepts.md b/sources/modules/compile-time-programming/concepts.md index 7e58a7e..cc9c122 100644 --- a/sources/modules/compile-time-programming/concepts.md +++ b/sources/modules/compile-time-programming/concepts.md @@ -1,3 +1,6 @@ -# Compile-time programming: concepts +## Compile-time programming: concepts + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ This topic is currently under construction and will soon be filled with information :) diff --git a/sources/modules/compile-time-programming/function-templates.md b/sources/modules/compile-time-programming/function-templates.md index c0bbb6f..bf0db25 100644 --- a/sources/modules/compile-time-programming/function-templates.md +++ b/sources/modules/compile-time-programming/function-templates.md @@ -1,3 +1,6 @@ -# Compile-time programming: Function Templates +## Compile-time programming: Function Templates + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ This topic is currently under construction and will soon be filled with information :) diff --git a/sources/modules/compile-time-programming/requires-clause.md b/sources/modules/compile-time-programming/requires-clause.md index b4d5a99..da90d76 100644 --- a/sources/modules/compile-time-programming/requires-clause.md +++ b/sources/modules/compile-time-programming/requires-clause.md @@ -1,4 +1,4 @@ -# Compile-time programming: requires-clause +## Compile-time programming: requires-clause _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/compile-time-programming/requires-expressions.md b/sources/modules/compile-time-programming/requires-expressions.md index 577430a..c517166 100644 --- a/sources/modules/compile-time-programming/requires-expressions.md +++ b/sources/modules/compile-time-programming/requires-expressions.md @@ -1,9 +1,13 @@ ## Module name: Requires Expressions {#req-expr} + _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ ### Overview +_Provides a short natural language abstract of the module’s contents._ +_Specifies the different levels of teaching._ + ------------------------------------------------------------------------- Level Objectives ------------------ ------------------------------------------------------ @@ -18,12 +22,17 @@ Advanced --- ### Motivation +_Why is this important?_ +_Why do we want to learn/teach this topic?_ + Requires-expressions allow a developer to perform compile-time evaluation on the validity of other expressions. These are fundamental to the ability to write concepts. [[Compile-time programming: concepts]][1] ## Topic introduction +_Very brief introduction to the topic._ + Requires-expressions are compile-time predicates which evaluate to true when their specified set of expressions are all valid for a given set of inputs. @@ -43,6 +52,10 @@ It is helpful if: #### Student outcomes +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + A student should be able to: 1. Write a simple-requirement to assert the validity of an expression @@ -53,6 +66,9 @@ A student should be able to: #### Caveats +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + To require that expressions, which evaluate to a boolean value like `sizeof(t) == 4`, evaluate to `true` a nested-requirement is needed (e.g., `requires sizeof(t) == 4;`). Omitting the `requires` results in a @@ -61,6 +77,8 @@ not on the result of the operation. #### Points to cover +_This section lists important details for each point._ + * All requires-expression requirements terminate with a semicolon. * simple-requirements are used to check that an expression is well-formed. * nested-requirements are introduced with `requires` and primarily used to check the result of an expression computable by the compiler, including concepts or other requires-expressions. @@ -69,9 +87,9 @@ not on the result of the operation. * Checks are performed by the compiler, not at run time. * If covering usage of requires-expression with requires-clause, [[Compile-time programming: requires clause]][3] demonstrate `requires requires` and show how to ever avoid writing it by using a concept. [[Compile-time programming: concepts]][1] -### Main: Advanced requirements {#req-expr-intermediate} +### Main: Advanced requirements {#req-expr-main} -#### Background/required knowledge +#### Background/Required Knowledge * All of the above. * Knowledge of `noexcept` @@ -82,6 +100,10 @@ A student is able to: #### Student outcomes +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + A student should be able to: 1. Write compound-requirements which test the `noexcept`ness of an expression. @@ -89,8 +111,13 @@ A student should be able to: #### Caveats +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + #### Points to cover +_This section lists important details for each point._ + * Compound-requirements allow the optional ability to test whether an expression is marked as `noexcept`, by using a trailing `noexcept` keyword. ```cpp diff --git a/sources/modules/functions/calling-functions.md b/sources/modules/functions/calling-functions.md index 183a5e5..ee90b37 100644 --- a/sources/modules/functions/calling-functions.md +++ b/sources/modules/functions/calling-functions.md @@ -1,4 +1,4 @@ -# C++ functions: calling functions +## C++ functions: calling functions _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/functions/defaulted-parameters.md b/sources/modules/functions/defaulted-parameters.md index 1e0ec3b..3194ce6 100644 --- a/sources/modules/functions/defaulted-parameters.md +++ b/sources/modules/functions/defaulted-parameters.md @@ -1,9 +1,13 @@ ## Functions: default argument {#func-args} + _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ ### Overview +_Provides a short natural language abstract of the module’s contents._ +_Specifies the different levels of teaching._ + Functions in C++ may be overloaded with different numbers and types of parameters. It may be of value to specify default arguments for some number of parameters, to allow a caller to avoid specifying arguments that @@ -23,12 +27,17 @@ Advanced refinement of default arguments through multiple ### Motivation +_Why is this important?_ +_Why do we want to learn/teach this topic?_ + Default arguments allow the omission of arguments with obvious or common values. Also may be utilized to extend an existing function signature without forcing changes to existing calling code. ### Topic introduction +_Very brief introduction to the topic._ + Explain how default arguments work and how to define them. ### Foundational: Using and defining functions with default arguments {#func-args-basic} @@ -44,6 +53,10 @@ A student is able to: #### Student outcomes +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + A student should be able to: 1. Call to a function with a default argument with or without that argument specified @@ -53,6 +66,9 @@ A student should be able to: #### Caveats +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + * When no forward-declaration exists, the definition serves as the declaration * When multiple declarations exist, only one may specify the default for any particular parameter, but multiple declarations may specify the defaults for different parameters. * Additional default values may be specified for other parameters in repeat declarations @@ -60,26 +76,40 @@ A student should be able to: #### Points to cover +_This section lists important details for each point._ + * Default value may only be specified once for each parameter among all declarations * Default values must start from the rightmost parameter and continue leftward without gaps * Considerations of when to use default arguments vs overload set -### Main: implementing * {#func-args-intermediate} +### Main: implementing * {#func-args-main} -#### Background/required knowledge +#### Background/Required knowledge * All of the above. #### Student outcomes +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + A student should be able to: #### Caveats +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + #### Points to cover +_This section lists important details for each point._ + ### Advanced {#func-args-advanced} +_These are important topics that are not expected to be covered but provide +guidance where one can continue to investigate this topic in more depth._ + Subsequent redeclarations of the same function may add default argument values, which are then usable by callers. Though a single parameter cannot be given a default argument twice in the same diff --git a/sources/modules/functions/member-functions.md b/sources/modules/functions/member-functions.md index cd67792..874011f 100644 --- a/sources/modules/functions/member-functions.md +++ b/sources/modules/functions/member-functions.md @@ -1,4 +1,4 @@ -# C++ functions: member functions +## C++ functions: member functions _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/functions/user-defined-literals.md b/sources/modules/functions/user-defined-literals.md index 22d32ed..bb1bcd6 100644 --- a/sources/modules/functions/user-defined-literals.md +++ b/sources/modules/functions/user-defined-literals.md @@ -84,7 +84,7 @@ implementation-defined, unspecified, or undefined behavior._ _This section lists important details for each point._ -### Main: implementing UDLs {#udl-intermediate} +### Main: implementing UDLs {#udl-main} #### Background/Required Knowledge diff --git a/sources/modules/meta-error-handling/static_assert.md b/sources/modules/meta-error-handling/static_assert.md index b82e12f..6b50e9e 100644 --- a/sources/modules/meta-error-handling/static_assert.md +++ b/sources/modules/meta-error-handling/static_assert.md @@ -69,7 +69,7 @@ _This section lists important details for each point._ * X * In addition to what is wrong, a good error message will inform the user of how to correct it -### Main: Contracts and `static_assert` {#static-assert-intermediate} +### Main: Contracts and `static_assert` {#static-assert-main} #### Background/Required Knowledge diff --git a/sources/modules/object-model/constant-objects.md b/sources/modules/object-model/constant-objects.md index 4e034eb..1815b1e 100644 --- a/sources/modules/object-model/constant-objects.md +++ b/sources/modules/object-model/constant-objects.md @@ -1,4 +1,4 @@ -# C++ object model: constant objects +## C++ object model: constant objects _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/constructors.md b/sources/modules/object-model/constructors.md index 6c08eb9..41d0ab6 100644 --- a/sources/modules/object-model/constructors.md +++ b/sources/modules/object-model/constructors.md @@ -1,4 +1,4 @@ -# C++ object model: constructor +## C++ object model: constructor _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/copy-semantics.md b/sources/modules/object-model/copy-semantics.md index 9036df2..d0cb180 100644 --- a/sources/modules/object-model/copy-semantics.md +++ b/sources/modules/object-model/copy-semantics.md @@ -80,7 +80,7 @@ _This section lists important details for each point._ * Strings (copies the value) -### Main: Implementing user-defined copy operations {#copy-intermediate} +### Main: Implementing user-defined copy operations {#copy-main} #### Background/Required Knowledge diff --git a/sources/modules/object-model/declarations.md b/sources/modules/object-model/declarations.md index c4497db..feee753 100644 --- a/sources/modules/object-model/declarations.md +++ b/sources/modules/object-model/declarations.md @@ -1,4 +1,4 @@ -# C++ object model: declarations +## C++ object model: declarations _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/move-semantics.md b/sources/modules/object-model/move-semantics.md index a0b4cc0..8172353 100644 --- a/sources/modules/object-model/move-semantics.md +++ b/sources/modules/object-model/move-semantics.md @@ -1,4 +1,4 @@ -# C++ object model: move semantics +## C++ object model: move semantics _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/objects.md b/sources/modules/object-model/objects.md index cb59892..31aa5b5 100644 --- a/sources/modules/object-model/objects.md +++ b/sources/modules/object-model/objects.md @@ -1,4 +1,4 @@ -# C++ object model: objects +## C++ object model: objects _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/rule-of-five.md b/sources/modules/object-model/rule-of-five.md index 78ef963..894ffdb 100644 --- a/sources/modules/object-model/rule-of-five.md +++ b/sources/modules/object-model/rule-of-five.md @@ -1,4 +1,4 @@ -# C++ object model: rule of five +## C++ object model: rule of five _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/rule-of-zero.md b/sources/modules/object-model/rule-of-zero.md index 8ee7520..66174ad 100644 --- a/sources/modules/object-model/rule-of-zero.md +++ b/sources/modules/object-model/rule-of-zero.md @@ -1,4 +1,4 @@ -# C++ object model: rule of zero +## C++ object model: rule of zero _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/special-member-functions.md b/sources/modules/object-model/special-member-functions.md index 091f31a..f67320b 100644 --- a/sources/modules/object-model/special-member-functions.md +++ b/sources/modules/object-model/special-member-functions.md @@ -1,4 +1,4 @@ -# C++ object model: special member functions +## C++ object model: special member functions _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/modules/object-model/types.md b/sources/modules/object-model/types.md index 4eaa1ab..1fcd0ea 100644 --- a/sources/modules/object-model/types.md +++ b/sources/modules/object-model/types.md @@ -1,4 +1,4 @@ -# C++ object model: types +## C++ object model: types _Skeleton descriptions are typeset in italic text,_ _so please don't remove these descriptions when editing the topic._ diff --git a/sources/obtaining_document.md b/sources/obtaining_document.md index b4d2a34..9fad82d 100644 --- a/sources/obtaining_document.md +++ b/sources/obtaining_document.md @@ -2,29 +2,24 @@ The most recent version of this document is available as an online HTML document at: -. +. The version of the document that you are currently reading is available in the following formats: 1. online (HTML) format as a single large HTML document: - - [later to be ?] + 2. EPUB format: - - [later to be ?] + 3. online (HTML) format, split across multiple HTML documents: - - [later to be ?] + **[Note: The support for this format needs more work (in order to beautify and fix linking issues).]** Older versions of this document are also available. In general version _ver_ is available at -https://mdadams.github.io/sg20_guidelines_for_teaching_cpp/_ver_. -For example, version v0.1.0 (assuming that this version exists) would be -available at -[later to be -?]. +https://cplusplus.github.io/SG20/_ver_/html. +For example, version 0.1.0 (assuming that this version exists) would be +available at . diff --git a/tools/build/aspell_frontend b/tools/build/aspell_frontend index a949b08..d78874f 100755 --- a/tools/build/aspell_frontend +++ b/tools/build/aspell_frontend @@ -1,16 +1,14 @@ #! /usr/bin/env bash -# Michael Adams (mdadams@ece.uvic.ca) cmd_dir="$(dirname "$0")" || exit 1 -# Get an absolute pathname. -#cmd_dir=$(realpath "$cmd_dir") || exit 1 - top_dir="$cmd_dir/../.." spellcheck_dir="$top_dir/config/spellcheck" wordlist="wordlist" if [ ! -f "$spellcheck_dir/$wordlist" ]; then - echo "ERROR" - exit 1 + echo "ERROR" + exit 1 fi -exec aspell --home-dir="$spellcheck_dir" --personal "$wordlist" "$@" +tmp_wordlist=$(mktemp /tmp/wordlist.XXXXXX) +cat $spellcheck_dir/wordlist $spellcheck_dir/ignored_words.txt > $tmp_wordlist +exec aspell --home-dir="$spellcheck_dir" --personal $tmp_wordlist --lang en "$@" diff --git a/tools/build/build b/tools/build/build index 1c4363b..c93473e 100755 --- a/tools/build/build +++ b/tools/build/build @@ -7,38 +7,37 @@ # Terminate with error. panic() { - echo "ERROR: $@" 1>&2 - exit 1 + echo "ERROR: $@" 1>&2 + exit 1 } # Print usage information. usage() { - echo "bad usage: $@" - cat <<- EOF - build - Build the document. + echo "bad usage: $@" + cat << EOF + build - Build the document. - Usage - ===== + Usage + ===== - $0 -d \$install_dir -z \$github_ref + $0 -d \$install_dir -v \$version - Options - ======= + Options + ======= - -d \$install_dir - Set the installation directory for the built release to \$install_dir. + -d \$install_dir + Set the installation directory for the built release to \$install_dir. - -z \$github_ref - Set the GitHub ref to $\github_ref. - (This is used to determine the version of the document being released.) + -v \$version + Set the document version $\version. - Examples - ======== + Examples + ======== - $0 -d /tmp/sg20_test/install -z refs/tags/v0.0.1 - EOF - exit 2 + $0 -d /tmp/sg20_test/install -v v0.0.1 +EOF + exit 2 } ################################################################################ @@ -53,41 +52,38 @@ source_dir="$cmd_dir/../.." ################################################################################ install_dir= -github_ref= +version= spellcheck=0 -while getopts d:z:s opt; do - case $opt in - d) - install_dir="$OPTARG";; - z) - github_ref="$OPTARG";; - s) - spellcheck=1;; - \?) - usage - break;; - esac +while getopts d:v:s opt; do + case $opt in + d) + install_dir="$OPTARG";; + v) + version="$OPTARG";; + s) + spellcheck=1;; + \?) + usage + break;; + esac done shift $((OPTIND - 1)) if [ -z "$install_dir" ]; then - usage "no output directory specified" + usage "no output directory specified" fi -if [ -z "$github_ref" ]; then - usage "no github ref specified" +if [ -z "$version" ]; then + usage "no document version specified" fi ################################################################################ # Build the document. ################################################################################ -version="$(awk -v FS="/" '{print $3;}' <<< "$github_ref")" || \ - panic "cannot determine document version" - if [ ! -d "$install_dir" ]; then - mkdir -p "$install_dir" || \ - panic "cannot make installation directory $install_dir" + mkdir -p "$install_dir" || \ + panic "cannot make installation directory $install_dir" fi (cd "$source_dir" && make clean) || \ @@ -97,5 +93,12 @@ fi make DOC_VERSION="$version" DOC_SPELLCHECK_MUST_PASS="$spellcheck" all) || \ panic "cannot build document" -make INSTALL_DIR="$install_dir" install || \ +make DOC_VERSION="$version" INSTALL_DIR="$install_dir/$version" install || \ panic "cannot install release" + +cp $source_dir/sources/index.html $install_dir + +pushd $install_dir +unlink latest +ln -s $version/html latest +popd diff --git a/tools/build/deploy b/tools/build/deploy deleted file mode 100755 index d88de76..0000000 --- a/tools/build/deploy +++ /dev/null @@ -1,312 +0,0 @@ -#! /usr/bin/env bash - -################################################################################ -# Some helper functions. -################################################################################ - -# Terminate with error. -panic() -{ - echo "ERROR: $@" 1>&2 - exit 1 -} - -join_by() -{ - local IFS="$1" - shift - echo "$*" -} - - -# Print usage information and exit. -usage() -{ - echo "bad usage: $@" - cat <<- EOF - deploy - deploy new release of document to GitHub Pages repository - - Usage - ===== - - $0 [options] - - Options - ======= - - -b \$branch - Set the deployment branch to \$branch. - -t \$tmp_dir - Set the temporary directory to $\tmp_dir. - -i \$in_dir - Set the input directory to \$in_dir. - This is the directory containing the output of the document build - process. - -r \$repo_name - Set the deployment repository to \$repo_name. - This includes both the user/organization and repository name. - -z \$github_ref - Set the GitHub ref to \$github_ref. - -f - Allow private key file to be overwritten. - -n - Prepare to push to deployment repository but do not actually - push the changes. (This is only for testing purposes.) - - Examples - ======== - - export DEPLOY_KEY=.... # set DEPLOY_KEY to private SSH key - $0 -n -f -r mdadams/sg20 -b gh-pages \\ - -t /tmp/sg20_test/tmp -i /tmp/sg20_test/install -z refs/tags/v1.0.0 - EOF - exit 2 -} - -################################################################################ -# Parse command line. -################################################################################ - -branch="gh-pages" -in_dir= -repo_name= -tmp_dir="${TMPDIR:-/tmp}" -github_ref= -force=0 -prepare_only=0 -verbose=1 -deploy_mode= - -while getopts b:i:r:t:z:fnvqm: opt; do - case $opt in - m) - deploy_mode="$OPTARG";; - v) - verbose=$((verbose + 1));; - q) - verbose=$((verbose - 1));; - b) - branch="$OPTARG";; - t) - tmp_dir="$OPTARG";; - i) - in_dir="$OPTARG";; - r) - repo_name="$OPTARG";; - z) - github_ref="$OPTARG";; - f) - force=1;; - n) - prepare_only=1;; - \?) - usage - break;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "$github_ref" ]; then - usage "no github ref specified" -fi -if [ -z "$tmp_dir" ]; then - usage "no temporary directory specified" -fi -if [ -z "$in_dir" ]; then - usage "no input directory specified" -fi -if [ -z "$repo_name" ]; then - usage "no repository name specified" -fi - -case "$deploy_mode" in -key) - if [ -z "$DEPLOY_KEY" ]; then - usage "DEPLOY_KEY environment variable not set" - fi - ;; -token) - if [ -z "$DEPLOY_TOKEN" ]; then - usage "DEPLOY_TOKEN environment variable not set" - fi - ;; -*) - usage "bad deployment mode specified $deploy_mode" - ;; -esac - -if [ "$verbose" -ge 1 ]; then - echo "temporary directory $tmp_dir" - echo "input directory $in_dir" - echo "repository name $repo_name" - echo "GitHub ref $github_ref" -fi - -################################################################################ -# Perform some basic initialization. -################################################################################ - -if [ "$verbose" -ge 3 ]; then - set -xv -fi - -cmd_dir=$(dirname "$0") || \ - panic "cannot determine directory" -top_dir="$cmd_dir/../.." - -version="$(awk -v FS="/" '{print $3;}' <<< "$github_ref")" || \ - panic "cannot determine document version" -if [ "$verbose" -ge 1 ]; then - echo "version $version" -fi - -tmp_dir="$tmp_dir/deploy" - -git_dir="$tmp_dir/git" -version_dir="$git_dir/$version" -github_host="github.com" -github_user="git" - -################################################################################ -# Setup SSH client configuration if needed. -################################################################################ - -case "$deploy_mode" in - -key) - - ssh_dir="$HOME/.ssh" - private_key_file="$ssh_dir/private_key" - - repo_url="ssh://git@$github_host/$repo_name.git" - - if [ "$force" -eq 0 -a -e "$private_key_file" ]; then - panic "private key file already exists" - fi - - if [ ! -d "$ssh_dir" ]; then - mkdir -p "$ssh_dir" || \ - panic "cannot make directory $ssh_dir" - fi - if [ ! -d "$tmp_dir" ]; then - mkdir -p "$ssh_dir" || \ - panic "cannot make directory $ssh_dir" - fi - - echo "$DEPLOY_KEY" > "$private_key_file" || \ - panic "cannot create private key file" - chmod u+rw,g=,o= "$private_key_file" || \ - panic "cannot set permissions for private key file" - - if [ -z "$SSH_AGENT_PID" ]; then - eval $(ssh-agent) || \ - panic "cannot start ssh-agent" - fi - ssh-add "$private_key_file" || \ - panic "ssh-add failed" - ;; - -token) - - git config --global credential.helper 'cache --timeout=86400' || \ - panic "cannot set credential helper" - cred_info=() - cred_info+=("protocol=https") - cred_info+=("host=$github_host") - cred_info+=("username=$github_user") - cred_info+=("password=$DEPLOY_TOKEN") - cred_string="$(join_by $'\n' "${cred_info[@]}")" || \ - panic "string processing failed" - cred_string="$(git credential fill <<< "$cred_string")" || \ - panic "git credential fill failed" - - git credential approve <<< "$cred_string" || \ - panic "git credential approve failed" - - repo_url="https://$github_user@$github_host/$repo_name.git" - ;; - -*) - - panic "unexpected case" - ;; - -esac - -if [ "$verbose" -ge 1 ]; then - echo "repository URL: $repo_url" -fi - -################################################################################ -# Add new release to GitHub pages repository. -################################################################################ - -git_push_extra_args=() - -# Clone the repository. -git clone "$repo_url" "$git_dir" || \ - panic "cannot clone repository $repo_url" - -# Create an orphaned branch for the web site content if the branch does -# not already exist. -git -C "$git_dir" ls-remote --exit-code --heads "$repo_url" "$branch" \ - > /dev/null -status=$? -if [ "$status" -ne 0 -a "$status" -ne 2 ]; then - panic "git ls-remote failed" -fi -if [ "$status" -eq 2 ]; then - git -C "$git_dir" checkout --orphan "$branch" || \ - panic "cannot create orphan branch $branch" - git_push_extra_args=(-u origin "$branch") -else - git -C "$git_dir" checkout "$branch" || \ - panic "cannot checkout branch $branch" -fi - -# Set the Git user. -git -C "$git_dir" config --local user.name "Michael Adams" || \ - panic "cannot set Git user name" -git -C "$git_dir" config --local user.email "mdadams@ece.uvic.ca" || \ - panic "cannot set Git user email" - -# Add the content for the new release. -if [ -e "$version_dir" ]; then - panic "directory already exist for $version" -fi -mkdir -p "$version_dir" || \ - panic "cannot make directory $version_dir" -(cd "$in_dir" && tar -cf - .) | (cd "$version_dir" && tar -xf -) || \ - panic "tar failed" - -# Update the symlink for the latest release. -target="$git_dir/latest" -if [ -h "$target" -o -e "$target" ]; then - rm -f "$target" || panic "rm failed" -fi -ln -s "$version/html" "$target" || \ - panic "ln failed" - -# Add a top-level index.html file to redirect to the latest release. -target="$git_dir/index.html" -if [ -e "$target" ]; then - rm -f "$target" || panic "cannot remove $target" -fi -cp "$top_dir/sources/index.html" "$target" || panic "cannot copy $target" - -if [ "$verbose" -ge 2 ]; then - ls -al "$git_dir"/* -fi - -# Commit and push all of the changes. -git -C "$git_dir" add . || \ - panic "git add failed" -git -C "$git_dir" commit -m "Deploying release $version" || \ - panic "git commit failed" -if [ "$prepare_only" -eq 0 ]; then - if [ "$verbose" -ge 1 ]; then - echo "Pushing changes." - fi - git -C "$git_dir" push "${git_push_extra_args[@]}" || \ - panic "git push failed" -fi diff --git a/tools/build/generate_main.py b/tools/build/generate_main.py new file mode 100755 index 0000000..0c6a783 --- /dev/null +++ b/tools/build/generate_main.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +""" +Generates a complete main file that includes all teaching modules. +""" +import typing as tp +from argparse import ArgumentParser +from pathlib import Path + + +def is_placeholder(module_file: Path) -> bool: + """ + Checks if a given module file is only a placeholder. + + Args: + module_file: that should be checked + """ + with open(module_file, "r", encoding="UTF-8") as module: + for line in module.readlines()[:7]: + if line.startswith("This topic is currently under construction"): + return True + + return False + + +def inject_teaching_modules(main_file: tp.TextIO, module_folder: Path) -> None: + """ + Injects '__INCLUDE__(module/file/path.md)' markers into the main file for + all modules found in the given module folder. + + Args: + main_file: where generated input is written to + module_folder: location of the teaching module folder + """ + for module in module_folder.glob('**/*.md'): + if not is_placeholder(module): + main_file.write(f"__INCLUDE__({module})\n\n") + + +def generate_main(raw_main_filepath: Path, output_filepath: Path, + module_folder: Path) -> None: + """ + Generate a complete main file that includes all teaching modules. + + Args: + raw_main: file that will be processed + output_filename: where generated input should be written + module_folder: location of the teaching module folder + """ + with open(raw_main_filepath, "r", encoding="UTF-8") as raw_main, open( + output_filepath, "w", encoding="UTF-8") as generated_main_file: + + for line in raw_main.readlines(): + if line.startswith("INJECT_TEACHING_MODULES"): + inject_teaching_modules(generated_main_file, module_folder) + else: + generated_main_file.write(line) + + +def main(): + """Parse inputs and kick of main generation.""" + parser = ArgumentParser("topic_updater") + parser.add_argument("--raw", + type=Path, + help="The raw main file to process.") + parser.add_argument("--out", + type=Path, + help="Output filename for the generated main.") + parser.add_argument("--module-folder", + type=Path, + help="Path to the module folder.") + + args = parser.parse_args() + + generate_main(args.raw, args.out, args.module_folder) + + +if __name__ == "__main__": + main() diff --git a/tools/build/make_markdown b/tools/build/make_markdown index e584380..c73deac 100755 --- a/tools/build/make_markdown +++ b/tools/build/make_markdown @@ -1,42 +1,31 @@ #! /usr/bin/env bash -# Michael Adams (mdadams@ece.uvic.ca) awk ' -function output(level, old_level, label, basic, intermediate, advanced, text) { - #bad_char = "🤢"; - #bad_char = "❌"; +function output(level, old_level, label, basic, main, advanced, text) { bad_char = "?"; - #check_char = "✓" check_char = "✔️"; emdash = "—"; if (level == 1) { if (label == "?") { label = bad_char; - #basic = emdash; - #intermediate = emdash; - #advanced = emdash; } else { if (basic == "y") { - #basic = sprintf("%s", label, check_char); basic = sprintf("[%s](#%s-basic)", check_char, label); } else if (basic == "n") { basic = emdash; } - if (intermediate == "y") { - #intermediate = sprintf("%s", label, check_char); - intermediate = sprintf("[%s](#%s-intermediate)", check_char, label); - } else if (intermediate == "n") { - intermediate = emdash; + if (main == "y") { + main = sprintf("[%s](#%s-main)", check_char, label); + } else if (main == "n") { + main = emdash; } if (advanced == "y") { - #advanced = sprintf("%s", label, check_char); advanced = sprintf("[%s](#%s-advanced)", check_char, label); } else if (advanced == "n") { advanced = emdash; } } - #label = sprintf("%s", label, label); label = sprintf("[%s](#%s)", label, label); } @@ -45,16 +34,13 @@ function output(level, old_level, label, basic, intermediate, advanced, text) { printf "|---|----------|---|---|---|\n"; } if (level == 1) { - printf "| [%s] | %s | %s | %s | %s |\n", label, text, basic, intermediate, advanced; + printf "| [%s] | %s | %s | %s | %s |\n", label, text, basic, main, advanced; } if ((level == 0 || level == -1) && old_level > 0) { printf "\n\n"; - #printf "Note: %s indicates no entry yet available\n\n", bad_char; - #printf "Note: The IDs and checkmarks (%s) are linked to the corresponding sections.\n\n", check_char; } if (level == 0) { printf "\n"; - #printf "## [%s] %s {#%s}\n", label, text, label; printf "## %s {#%s}\n", text, label; } } @@ -72,7 +58,7 @@ BEGIN { label = ""; basic = ""; - intermediate = ""; + main = ""; advanced = ""; text = ""; @@ -80,17 +66,17 @@ BEGIN { label = $1; text = substr($0, index($0, $2)); - output(level, old_level, label, basic, intermediate, advanced, text); + output(level, old_level, label, basic, main, advanced, text); } else if (level == 1) { label = $1; basic = $2; - intermediate = $3; + main = $3; advanced = $4; text = substr($0, index($0, $5)); - output(level, old_level, label, basic, intermediate, advanced, text); + output(level, old_level, label, basic, main, advanced, text); } @@ -104,11 +90,11 @@ END { label = ""; basic = ""; - intermediate = ""; + main = ""; advanced = ""; text = ""; - output(level, old_level, label, basic, intermediate, advanced, text); + output(level, old_level, label, basic, main, advanced, text); } diff --git a/tools/build/preprocessor b/tools/build/preprocessor index ee857e3..c7903fb 100755 --- a/tools/build/preprocessor +++ b/tools/build/preprocessor @@ -1,5 +1,4 @@ #! /usr/bin/env bash -# Michael Adams (mdadams@ece.uvic.ca) program_dir="$(dirname "$0")" || exit 1 program_pathname="$0" diff --git a/tools/mgmt_tools/topic_updater.py b/tools/mgmt_tools/topic_updater.py index 01757b8..8fadf13 100755 --- a/tools/mgmt_tools/topic_updater.py +++ b/tools/mgmt_tools/topic_updater.py @@ -110,7 +110,7 @@ def lookup_heading(self, start_heading_line: str) -> SectionHeading: def get_title_heading(self) -> SectionHeading: """ The Title heading of the document. """ - if not self.headings[0].header_text.startswith("# "): + if not self.headings[0].header_text.startswith("## "): raise AssertionError( "First heading in the skeleton was not the title.") return self.headings[0] @@ -132,9 +132,13 @@ def check_if_topic_file_matches(self, topic_file: tp.TextIO) -> bool: expected_meta_text: tp.List[str] = [] for line in topic_file.readlines(): - if line.startswith("#"): + line = line.rsplit("{#")[0] # ignore anchors + + if line.startswith("This topic is currently under construction"): + return True + if line.startswith("##"): if current_heading and expected_meta_text: - print("Found missing italics:") + print("Missing italics detected:") print(f"Expected: {expected_meta_text[0]}") return False @@ -144,7 +148,7 @@ def check_if_topic_file_matches(self, topic_file: tp.TextIO) -> bool: processing_meta_text = False # Check if the required section headings are present - if line.startswith("##") and current_heading: + if line.startswith("###") and current_heading: if current_heading.header_text.split(":")[0] != line.split( ":")[0].strip(): print("Found wrong section title:") @@ -217,7 +221,7 @@ def __process_existing_topic_content( emitting_doc_text = True for line in topic_file.readlines(): - if line.startswith("##"): + if line.startswith("###"): next_heading = next(skeleton_headings_iter) current_heading = self.lookup_heading(line.split(":")[0]) @@ -241,7 +245,7 @@ def __process_existing_topic_content( updated_topic_lines.extend( current_heading.convert_meta_text_to_lines()) - elif line.startswith("#"): + elif line.startswith("##"): # Verify that the title heading has correct meta text emitting_doc_text = False next_heading = next(skeleton_headings_iter) @@ -319,9 +323,11 @@ def check_skeletons(skeleton: Skeleton, topic_paths: tp.List[Path]) -> bool: all_files_matched = True for topic_path in topic_paths: with open(topic_path, "r") as topic_file: + print(f"Checking: {topic_path}") if skeleton.check_if_topic_file_matches(topic_file): - print(f"All meta-text in {topic_path} matched the skeleton.") + print(" └─> All meta-text matched the skeleton.") else: + print(" └─> Errors where found!") all_files_matched = False return all_files_matched diff --git a/tools/old/Readme.md b/tools/old/Readme.md deleted file mode 100644 index 31a5bfa..0000000 --- a/tools/old/Readme.md +++ /dev/null @@ -1,50 +0,0 @@ - - -# SG20: Teaching Topics - -## Modules: -* [Compile-time programming](#compile-time-programming) -* [Meta-error handling](#meta-error-handling) -* [C++ object model](#c-object-model) -* [Program design](#program-design) -* [User-defined types](#user-defined-types) -* [Functions](#functions) - -### Compile-time programming -* [requires-clause](sources/modules/compile-time-programming/requires-clause.md) -* [concepts](sources/modules/compile-time-programming/concepts.md) -* [requires-expressions](sources/modules/compile-time-programming/requires-expressions.md) -* [function-templates](sources/modules/compile-time-programming/function-templates.md) - -### Meta-error handling -* [static_assert](sources/modules/meta-error-handling/static_assert.md) - -### C++ object model -* [rule-of-five](sources/modules/object-model/rule-of-five.md) -* [constant-objects](sources/modules/object-model/constant-objects.md) -* [special-member-functions](sources/modules/object-model/special-member-functions.md) -* [declarations](sources/modules/object-model/declarations.md) -* [objects](sources/modules/object-model/objects.md) -* [constructors](sources/modules/object-model/constructors.md) -* [types](sources/modules/object-model/types.md) -* [copy-semantics](sources/modules/object-model/copy-semantics.md) -* [rule-of-zero](sources/modules/object-model/rule-of-zero.md) -* [move-semantics](sources/modules/object-model/move-semantics.md) - -### Program design -* [concepts](compile-time-programmings/concepts.md) - -### User-defined types -* [user-defined-literals](functions/user-defined-literals.md) - -### Functions -* [defaulted-parameters](sources/modules/functions/defaulted-parameters.md) -* [calling-functions](sources/modules/functions/calling-functions.md) -* [user-defined-literals](sources/modules/functions/user-defined-literals.md) -* [member-functions](sources/modules/functions/member-functions.md) - diff --git a/tools/pytest.ini b/tools/pytest.ini index 563a728..6a19603 100644 --- a/tools/pytest.ini +++ b/tools/pytest.ini @@ -1,4 +1,4 @@ [pytest] testpaths = tests addopts = --doctest-modules --doctest-continue-on-failure -python_paths = ../ +pythonpath = . diff --git a/tools/requirements.txt b/tools/requirements.txt index 6f03bf7..7282dee 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,3 +1,2 @@ -pytest -pytest-pythonpath +pytest>=7 mock diff --git a/tools/tests/TEST_INPUTS/fix_wrong_sections.md b/tools/tests/TEST_INPUTS/fix_wrong_sections.md index 69a0d9c..ae036a0 100644 --- a/tools/tests/TEST_INPUTS/fix_wrong_sections.md +++ b/tools/tests/TEST_INPUTS/fix_wrong_sections.md @@ -1,24 +1,24 @@ -# Main Title +## Main Title _Skeleton instructions are typeset in italic text._ -## Section with user content: provided by the user +### Section with user content: provided by the user _ Example section where user text is in the header _ -## Missing mid section +### Missing mid section _ This section could be missing in the middle of the topics _ -## Italics text should be updated +### Italics text should be updated _ Wrong old italics text. _ -## Meta text with line breaks +### Meta text with line breaks _ Some text here some after the line break _ -## Missing end section +### Missing end section _ This section could be missing at the end of the document _ diff --git a/tools/tests/TEST_INPUTS/missing_sections.md b/tools/tests/TEST_INPUTS/missing_sections.md index b97da9e..b10a759 100644 --- a/tools/tests/TEST_INPUTS/missing_sections.md +++ b/tools/tests/TEST_INPUTS/missing_sections.md @@ -1,16 +1,16 @@ -# Main Title +## Main Title _Skeleton instructions are typeset in italic text._ -## Section with user content: provided by the user +### Section with user content: provided by the user _ Example section where user text is in the header _ -## Italics text should be updated +### Italics text should be updated _ Updated italics text. _ -## Meta text with line breaks +### Meta text with line breaks _ Some text here some after the line break _ diff --git a/tools/tests/TEST_INPUTS/test_injectable_main.md b/tools/tests/TEST_INPUTS/test_injectable_main.md new file mode 100644 index 0000000..6f960b1 --- /dev/null +++ b/tools/tests/TEST_INPUTS/test_injectable_main.md @@ -0,0 +1 @@ +INJECT_TEACHING_MODULES_HERE diff --git a/tools/tests/TEST_INPUTS/test_modules/test_empty_module.md b/tools/tests/TEST_INPUTS/test_modules/test_empty_module.md new file mode 100644 index 0000000..1fac8ed --- /dev/null +++ b/tools/tests/TEST_INPUTS/test_modules/test_empty_module.md @@ -0,0 +1,84 @@ +## Test: some empty test module + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ + +### Overview + +_Provides a short natural language abstract of the module’s contents._ +_Specifies the different levels of teaching._ + +------------------------------------------------------------------------ +Level Objective +----------------- ------------------------------------------------------ +Foundational --- + +Main --- + +Advanced --- + +------------------------------------------------------------------------ + +### Motivation + +_Why is this important?_ +_Why do we want to learn/teach this topic?_ + +### Topic introduction + +_Very brief introduction to the topic._ + +### Foundational: Using * + +#### Background/Required Knowledge + +A student: + + +#### Student outcomes + +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + +A student should be able to: + +1. +2. +3. +4. +5. + +#### Caveats + +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + +#### Points to cover + +_This section lists important details for each point._ + +### Main: implementing * + +#### Background/Required Knowledge + +* All of the above. + +#### Student outcomes + +A student should be able to: + +1. +2. +3. +4. +5. + +#### Caveats + +#### Points to cover + +### Advanced + +_These are important topics that are not expected to be covered but provide +guidance where one can continue to investigate this topic in more depth._ diff --git a/tools/tests/TEST_INPUTS/test_modules/test_placeholder.md b/tools/tests/TEST_INPUTS/test_modules/test_placeholder.md new file mode 100644 index 0000000..b1800e6 --- /dev/null +++ b/tools/tests/TEST_INPUTS/test_modules/test_placeholder.md @@ -0,0 +1,6 @@ +## Test: placeholder + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ + +This topic is currently under construction and will soon be filled with information :) diff --git a/tools/tests/TEST_INPUTS/test_modules/test_wrong_placeholder.md b/tools/tests/TEST_INPUTS/test_modules/test_wrong_placeholder.md new file mode 100644 index 0000000..069670c --- /dev/null +++ b/tools/tests/TEST_INPUTS/test_modules/test_wrong_placeholder.md @@ -0,0 +1,6 @@ +## Test: placeholder + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ + +Wrong marker text :( diff --git a/tools/tests/TEST_INPUTS/test_skeleton.md b/tools/tests/TEST_INPUTS/test_skeleton.md index f32638c..972227f 100644 --- a/tools/tests/TEST_INPUTS/test_skeleton.md +++ b/tools/tests/TEST_INPUTS/test_skeleton.md @@ -1,24 +1,24 @@ -# Main Title +## Main Title _Skeleton instructions are typeset in italic text._ -## Section with user content: provided by the user +### Section with user content: provided by the user _ Example section where user text is in the header _ -## Missing mid section +### Missing mid section _ This section could be missing in the middle of the topics _ -## Italics text should be updated +### Italics text should be updated _ Updated italics text. _ -## Meta text with line breaks +### Meta text with line breaks _ Some text here some after the line break _ -## Missing end section +### Missing end section _ This section could be missing at the end of the document _ diff --git a/tools/tests/TEST_INPUTS/user_content_heading_topic.md b/tools/tests/TEST_INPUTS/user_content_heading_topic.md index fbc7d52..679ed99 100644 --- a/tools/tests/TEST_INPUTS/user_content_heading_topic.md +++ b/tools/tests/TEST_INPUTS/user_content_heading_topic.md @@ -1,8 +1,8 @@ -# Main Title +## Main Title _Skeleton instructions are typeset in italic text._ -## Section with user content: This is provided by the user +### Section with user content: This is provided by the user _ Example section where user text is in the header _ @@ -10,19 +10,19 @@ Users can add different content here.
-## Missing mid section +### Missing mid section _ This section could be missing in the middle of the topics _ -## Italics text should be updated +### Italics text should be updated _ Updated italics text. _ -## Meta text with line breaks +### Meta text with line breaks _ Some text here some after the line break _ -## Missing end section +### Missing end section _ This section could be missing at the end of the document _ diff --git a/tools/tests/generate_main_test.py b/tools/tests/generate_main_test.py new file mode 100644 index 0000000..110c412 --- /dev/null +++ b/tools/tests/generate_main_test.py @@ -0,0 +1,59 @@ +""" +""" + +from pathlib import Path +import unittest +import io + +import build.generate_main as gm + +TEST_INPUTS = Path("tests/TEST_INPUTS/") + + +def _trim_include(line: str) -> str: + return line[12:-1] + + +class TestMainGeneration(unittest.TestCase): + """ + Tests the main generator functionallity that is used to automatically + generate a complete main file with all modules. + """ + def test_is_placeholder_actual_placeholder(self): + """Check is_placeholder correctly identifies a placeholder file.""" + self.assertTrue( + gm.is_placeholder(TEST_INPUTS / "test_modules" / + "test_placeholder.md")) + + def test_is_placeholder_does_not_identify_other_files(self): + """Check is_placeholder does not identifies other files as + placeholders.""" + self.assertFalse( + gm.is_placeholder(TEST_INPUTS / "test_modules" / + "test_wrong_placeholder.md")) + + def test_that_inject_modules_correctly_injects_includes(self): + """ + Checks that `inject_teaching_modules` correctly injects all available + teaching modules into the given file. + """ + output_buffer = io.StringIO() + gm.inject_teaching_modules(output_buffer, TEST_INPUTS / "test_modules") + + generated_include_lines = [ + s for s in output_buffer.getvalue().splitlines() if s + ] + + for include_line in generated_include_lines: + self.assertTrue(include_line.startswith("__INCLUDE__(")) + self.assertTrue(include_line.endswith(")")) + + generated_include_lines = sorted( + list(map(_trim_include, generated_include_lines))) + + self.assertEqual( + generated_include_lines[0], + "tests/TEST_INPUTS/test_modules/test_empty_module.md") + self.assertEqual( + generated_include_lines[1], + "tests/TEST_INPUTS/test_modules/test_wrong_placeholder.md") diff --git a/tools/tests/topic_updater_test.py b/tools/tests/topic_updater_test.py index f5349cb..063e3a0 100644 --- a/tools/tests/topic_updater_test.py +++ b/tools/tests/topic_updater_test.py @@ -11,7 +11,7 @@ import mock -import topic_updater as tu +import mgmt_tools.topic_updater as tu TEST_INPUTS = Path("tests/TEST_INPUTS/") @@ -78,31 +78,32 @@ def test_actual_skeleton_parse(self): "skeleton.md") self.assertEqual(actual_skeleton.get_title_heading().header_text, - "# Module name: topic name") + "## Module name: topic name") headings = list( map(lambda heading: heading.header_text, actual_skeleton.headings)) - self.assertEqual(headings[0], "# Module name: topic name") - self.assertEqual(headings[1], "## Overview") + self.assertEqual(headings[0], "## Module name: topic name") + self.assertEqual(headings[1], "### Overview") - self.assertEqual(headings[-2], "### Points to cover") - self.assertEqual(headings[-1], "## Advanced") + self.assertEqual(headings[-2], "#### Points to cover") + self.assertEqual(headings[-1], "### Advanced") def test_heading_lookup(self): """ Checks if we can lookup different section headings in the skeleton. """ section_with_user_content = self.test_skeleton.lookup_heading( - "## Section with user content:") + "### Section with user content:") missing_mid = self.test_skeleton.lookup_heading( - "## Missing mid section") + "### Missing mid section") - self.assertEqual(section_with_user_content.header_text, - "## Section with user content: provided by the user") + self.assertEqual( + section_with_user_content.header_text, + "### Section with user content: provided by the user") self.assertEqual( section_with_user_content.meta_text[0].rstrip(), "_ Example section where user text is in the header _") - self.assertEqual(missing_mid.header_text, "## Missing mid section") + self.assertEqual(missing_mid.header_text, "### Missing mid section") self.assertEqual( missing_mid.meta_text[0].rstrip(), "_ This section could be missing in the middle of the topics _") @@ -113,18 +114,19 @@ def test_heading_lookup_wrong_capitalization(self): even if the original text is wrongly capitialized. """ wrong_upper_case_heading = self.test_skeleton.lookup_heading( - "## Section with User content:") + "### Section with User content:") wrong_lower_case_heading = self.test_skeleton.lookup_heading( - "## missing mid section") + "### missing mid section") - self.assertEqual(wrong_upper_case_heading.header_text, - "## Section with user content: provided by the user") + self.assertEqual( + wrong_upper_case_heading.header_text, + "### Section with user content: provided by the user") self.assertEqual( wrong_upper_case_heading.meta_text[0].rstrip(), "_ Example section where user text is in the header _") self.assertEqual(wrong_lower_case_heading.header_text, - "## Missing mid section") + "### Missing mid section") self.assertEqual( wrong_lower_case_heading.meta_text[0].rstrip(), "_ This section could be missing in the middle of the topics _") @@ -134,7 +136,7 @@ def test_title_heading_lookup(self): Checks if we get the correct title heading from the test skeleton. """ self.assertEqual(self.test_skeleton.get_title_heading().header_text, - "# Main Title") + "## Main Title") self.assertEqual( self.test_skeleton.get_title_heading().meta_text[0], "_Skeleton instructions are typeset in italic text._" + os.linesep) @@ -145,7 +147,7 @@ def test_that_meta_text_with_linebreaks_inbetween_meta_markers_parse(self): lines. """ multi_line_meta_heading = self.test_skeleton.lookup_heading( - "## Meta text with line breaks") + "### Meta text with line breaks") self.assertEqual(multi_line_meta_heading.meta_text[0], "_ Some text here" + os.linesep) @@ -177,7 +179,8 @@ def test_not_to_override_user_provided_heading_text(self): start_test_scope_lines = list( dropwhile( lambda line: not line.startswith( - "## Section with user content:"), updated_topic_lines)) + "### Section with user content:"), + updated_topic_lines)) self.assertTrue( start_test_scope_lines[0].endswith( @@ -209,7 +212,7 @@ def test_that_we_dont_override_user_text_in_sections(self): self.assertTrue(start_test_scope_lines[2].startswith(""), "User provided content was modified") - @mock.patch('topic_updater._cli_yn_choice') + @mock.patch('mgmt_tools.topic_updater._cli_yn_choice') def test_if_missing_section_gets_added_to_the_end(self, mock_cli_yn): """ Checks if a section that is missing at the end of the topic file gets @@ -222,13 +225,13 @@ def test_if_missing_section_gets_added_to_the_end(self, mock_cli_yn): topic_file) self.assertEqual( - updated_topic_lines[-3].rstrip(), "## Missing end section", + updated_topic_lines[-3].rstrip(), "### Missing end section", "Missing section was not added to the end of the file.") self.assertEqual( updated_topic_lines[-1].rstrip(), "_ This section could be missing at the end of the document _") - @mock.patch('topic_updater._cli_yn_choice') + @mock.patch('mgmt_tools.topic_updater._cli_yn_choice') def test_if_missing_section_gets_added_in_the_middle(self, mock_cli_yn): """ Checks if a section that is missing in the middle of the topic file @@ -246,27 +249,27 @@ def test_if_missing_section_gets_added_in_the_middle(self, mock_cli_yn): # section. start_test_scope_lines = list( filter( - lambda line: line.startswith("##"), + lambda line: line.startswith("###"), dropwhile( lambda line: not line.startswith( - "## Section with user content"), + "### Section with user content"), iter(updated_topic_lines)))) # Verify that the previous section is correct self.assertTrue( start_test_scope_lines[0].startswith( - "## Section with user content"), + "### Section with user content"), "The section before the added missing section is wrong.") # Verify the section was inserted self.assertEqual( - start_test_scope_lines[1].rstrip(), "## Missing mid section", + start_test_scope_lines[1].rstrip(), "### Missing mid section", "The missing section was not inserted correctly.") # Verify the next section is correct self.assertTrue( start_test_scope_lines[2].startswith( - "## Italics text should be updated"), + "### Italics text should be updated"), "The section after the added missing section is wrong.") def test_that_meta_text_gets_updated(self): @@ -282,11 +285,11 @@ def test_that_meta_text_gets_updated(self): topic_lines_starting_from_italics_section = list( dropwhile( lambda line: not line.startswith( - "## Italics text should be updated"), + "### Italics text should be updated"), iter(updated_topic_lines))) self.assertEqual( topic_lines_starting_from_italics_section[0].rstrip(), - "## Italics text should be updated", + "### Italics text should be updated", "Could not find italics section.") self.assertEqual( topic_lines_starting_from_italics_section[2].rstrip(), @@ -314,7 +317,7 @@ def test_newline_between_heading_and_meta_text(self): start_test_scope_lines = list( dropwhile( - lambda line: not line.startswith("## Section with user"), + lambda line: not line.startswith("### Section with user"), updated_topic_lines)) self.assertEqual(start_test_scope_lines[1], os.linesep, "No newline between heading and meta text.")