From 74fed2b6a6b27ca55322d46e3c8f21f2efb67f42 Mon Sep 17 00:00:00 2001 From: Dima Pylypenko Date: Tue, 24 Feb 2026 14:48:32 +0200 Subject: [PATCH 01/49] Initial commit --- .gitignore | 41 +++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 42 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4fb281 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# debug information files +*.dwo diff --git a/README.md b/README.md new file mode 100644 index 0000000..f96fe50 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY \ No newline at end of file From 2fe184fe4478f1d44d94c78cb9679f6d52b7eb4c Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 15:36:29 +0200 Subject: [PATCH 02/49] chore: scaffold C++ dev container - Add .devcontainer with Ubuntu noble Dockerfile installing gcc, clang, clangd, make, cmake, clang-format, cmake-format, and ninja-build from apt-packages.in, with UTF-8 locale and a non-root user matching the host user. - Configure devcontainer.json to bind-mount the workspace, host SSH keys, and a persistent bash history, and preload VS Code extensions plus clangd/cmake-format editor defaults. - Ship root-level .clang-format, .clangd, and .cmake-format.json into the image so editor integrations and SCA tooling pick up the intended configs by default. - Add scripts/initialize host hook that creates the persistent bash history file under ~/.devcontainers before container start so shell history survives rebuilds. --- .devcontainer/.clang-format | 182 ++++++++++++++++++ .devcontainer/.clangd | 4 + .devcontainer/.cmake-format.json | 319 +++++++++++++++++++++++++++++++ .devcontainer/Dockerfile | 32 ++++ .devcontainer/apt-packages.in | 14 ++ .devcontainer/devcontainer.json | 67 +++++++ .devcontainer/scripts/initialize | 6 + .gitignore | 80 ++++++++ 8 files changed, 704 insertions(+) create mode 100644 .devcontainer/.clang-format create mode 100644 .devcontainer/.clangd create mode 100644 .devcontainer/.cmake-format.json create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/apt-packages.in create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/scripts/initialize create mode 100644 .gitignore diff --git a/.devcontainer/.clang-format b/.devcontainer/.clang-format new file mode 100644 index 0000000..d4c6dda --- /dev/null +++ b/.devcontainer/.clang-format @@ -0,0 +1,182 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Stroustrup +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 140 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: "^<.*" + Priority: 2 + SortPriority: 0 + - Regex: ".*" + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: "([-_](test|unittest))?$" +IncludeIsMainSourceRegex: "" +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - "c++" + - "C++" + CanonicalDelimiter: "" + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: "" + BasedOnStyle: google +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE +--- diff --git a/.devcontainer/.clangd b/.devcontainer/.clangd new file mode 100644 index 0000000..8a3e3af --- /dev/null +++ b/.devcontainer/.clangd @@ -0,0 +1,4 @@ +InlayHints: + Enabled: Yes + ParameterNames: No + DeducedTypes: Yes diff --git a/.devcontainer/.cmake-format.json b/.devcontainer/.cmake-format.json new file mode 100644 index 0000000..6a97078 --- /dev/null +++ b/.devcontainer/.cmake-format.json @@ -0,0 +1,319 @@ +{ + "_help_parse": "Options affecting listfile parsing", + "parse": { + "_help_additional_commands": [ + "Specify structure for custom cmake functions" + ], + "additional_commands": { + "qt_add_qml_module": { + "flags": [ + "SHARED", + "STATIC" + ], + "kwargs": { + "URI": "*", + "VERSION": "*", + "RESOURCE_PREFIX": "*", + "SOURCES": "*", + "QML_FILES": "*" + } + }, + "qt_add_resources": { + "kwargs": { + "PREFIX": "*", + "FILES": "*" + } + } + }, + "_help_override_spec": [ + "Override configurations per-command where available" + ], + "override_spec": {}, + "_help_vartags": [ + "Specify variable tags." + ], + "vartags": [], + "_help_proptags": [ + "Specify property tags." + ], + "proptags": [] + }, + "_help_format": "Options affecting formatting.", + "format": { + "_help_disable": [ + "Disable formatting entirely, making cmake-format a no-op" + ], + "disable": false, + "_help_line_width": [ + "How wide to allow formatted cmake files" + ], + "line_width": 100, + "_help_tab_size": [ + "How many spaces to tab for indent" + ], + "tab_size": 4, + "_help_use_tabchars": [ + "If true, lines are indented using tab characters (utf-8", + "0x09) instead of space characters (utf-8 0x20).", + "In cases where the layout would require a fractional tab", + "character, the behavior of the fractional indentation is", + "governed by " + ], + "use_tabchars": false, + "_help_fractional_tab_policy": [ + "If is True, then the value of this variable", + "indicates how fractional indentions are handled during", + "whitespace replacement. If set to 'use-space', fractional", + "indentation is left as spaces (utf-8 0x20). If set to", + "`round-up` fractional indentation is replaced with a single", + "tab character (utf-8 0x09) effectively shifting the column", + "to the next tabstop" + ], + "fractional_tab_policy": "use-space", + "_help_max_subgroups_hwrap": [ + "If an argument group contains more than this many sub-groups", + "(parg or kwarg groups) then force it to a vertical layout." + ], + "max_subgroups_hwrap": 2, + "_help_max_pargs_hwrap": [ + "If a positional argument group contains more than this many", + "arguments, then force it to a vertical layout." + ], + "max_pargs_hwrap": 6, + "_help_max_rows_cmdline": [ + "If a cmdline positional group consumes more than this many", + "lines without nesting, then invalidate the layout (and nest)" + ], + "max_rows_cmdline": 2, + "_help_separate_ctrl_name_with_space": [ + "If true, separate flow control names from their parentheses", + "with a space" + ], + "separate_ctrl_name_with_space": false, + "_help_separate_fn_name_with_space": [ + "If true, separate function names from parentheses with a", + "space" + ], + "separate_fn_name_with_space": false, + "_help_dangle_parens": [ + "If a statement is wrapped to more than one line, than dangle", + "the closing parenthesis on its own line." + ], + "dangle_parens": false, + "_help_dangle_align": [ + "If the trailing parenthesis must be 'dangled' on its on", + "line, then align it to this reference: `prefix`: the start", + "of the statement, `prefix-indent`: the start of the", + "statement, plus one indentation level, `child`: align to", + "the column of the arguments" + ], + "dangle_align": "prefix", + "_help_min_prefix_chars": [ + "If the statement spelling length (including space and", + "parenthesis) is smaller than this amount, then force reject", + "nested layouts." + ], + "min_prefix_chars": 4, + "_help_max_prefix_chars": [ + "If the statement spelling length (including space and", + "parenthesis) is larger than the tab width by more than this", + "amount, then force reject un-nested layouts." + ], + "max_prefix_chars": 10, + "_help_max_lines_hwrap": [ + "If a candidate layout is wrapped horizontally but it exceeds", + "this many lines, then reject the layout." + ], + "max_lines_hwrap": 2, + "_help_line_ending": [ + "What style line endings to use in the output." + ], + "line_ending": "unix", + "_help_command_case": [ + "Format command names consistently as 'lower' or 'upper' case" + ], + "command_case": "canonical", + "_help_keyword_case": [ + "Format keywords consistently as 'lower' or 'upper' case" + ], + "keyword_case": "unchanged", + "_help_always_wrap": [ + "A list of command names which should always be wrapped" + ], + "always_wrap": [], + "_help_enable_sort": [ + "If true, the argument lists which are known to be sortable", + "will be sorted lexicographicall" + ], + "enable_sort": true, + "_help_autosort": [ + "If true, the parsers may infer whether or not an argument", + "list is sortable (without annotation)." + ], + "autosort": false, + "_help_require_valid_layout": [ + "By default, if cmake-format cannot successfully fit", + "everything into the desired linewidth it will apply the", + "last, most agressive attempt that it made. If this flag is", + "True, however, cmake-format will print error, exit with non-", + "zero status code, and write-out nothing" + ], + "require_valid_layout": false, + "_help_layout_passes": [ + "A dictionary mapping layout nodes to a list of wrap", + "decisions. See the documentation for more information." + ], + "layout_passes": {} + }, + "_help_markup": "Options affecting comment reflow and formatting.", + "markup": { + "_help_bullet_char": [ + "What character to use for bulleted lists" + ], + "bullet_char": "*", + "_help_enum_char": [ + "What character to use as punctuation after numerals in an", + "enumerated list" + ], + "enum_char": ".", + "_help_first_comment_is_literal": [ + "If comment markup is enabled, don't reflow the first comment", + "block in each listfile. Use this to preserve formatting of", + "your copyright/license statements." + ], + "first_comment_is_literal": false, + "_help_literal_comment_pattern": [ + "If comment markup is enabled, don't reflow any comment block", + "which matches this (regex) pattern. Default is `None`", + "(disabled)." + ], + "literal_comment_pattern": null, + "_help_fence_pattern": [ + "Regular expression to match preformat fences in comments", + "default= ``r'^\\s*([`~]{3}[`~]*)(.*)$'``" + ], + "fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$", + "_help_ruler_pattern": [ + "Regular expression to match rulers in comments default=", + "``r'^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'``" + ], + "ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$", + "_help_explicit_trailing_pattern": [ + "If a comment line matches starts with this pattern then it", + "is explicitly a trailing comment for the preceeding", + "argument. Default is '#<'" + ], + "explicit_trailing_pattern": "#<", + "_help_hashruler_min_length": [ + "If a comment line starts with at least this many consecutive", + "hash characters, then don't lstrip() them off. This allows", + "for lazy hash rulers where the first hash char is not", + "separated by space" + ], + "hashruler_min_length": 10, + "_help_canonicalize_hashrulers": [ + "If true, then insert a space between the first hash char and", + "remaining hash chars in a hash ruler, and normalize its", + "length to fill the column" + ], + "canonicalize_hashrulers": true, + "_help_enable_markup": [ + "enable comment markup parsing and reflow" + ], + "enable_markup": true + }, + "_help_lint": "Options affecting the linter", + "lint": { + "_help_disabled_codes": [ + "a list of lint codes to disable" + ], + "disabled_codes": [], + "_help_function_pattern": [ + "regular expression pattern describing valid function names" + ], + "function_pattern": "[0-9a-z_]+", + "_help_macro_pattern": [ + "regular expression pattern describing valid macro names" + ], + "macro_pattern": "[0-9A-Z_]+", + "_help_global_var_pattern": [ + "regular expression pattern describing valid names for", + "variables with global (cache) scope" + ], + "global_var_pattern": "[A-Z][0-9A-Z_]+", + "_help_internal_var_pattern": [ + "regular expression pattern describing valid names for", + "variables with global scope (but internal semantic)" + ], + "internal_var_pattern": "_[A-Z][0-9A-Z_]+", + "_help_local_var_pattern": [ + "regular expression pattern describing valid names for", + "variables with local scope" + ], + "local_var_pattern": "[a-z][a-z0-9_]+", + "_help_private_var_pattern": [ + "regular expression pattern describing valid names for", + "privatedirectory variables" + ], + "private_var_pattern": "_[0-9a-z_]+", + "_help_public_var_pattern": [ + "regular expression pattern describing valid names for public", + "directory variables" + ], + "public_var_pattern": "[A-Z][0-9A-Z_]+", + "_help_argument_var_pattern": [ + "regular expression pattern describing valid names for", + "function/macro arguments and loop variables." + ], + "argument_var_pattern": "[a-z][a-z0-9_]+", + "_help_keyword_pattern": [ + "regular expression pattern describing valid names for", + "keywords used in functions or macros" + ], + "keyword_pattern": "[A-Z][0-9A-Z_]+", + "_help_max_conditionals_custom_parser": [ + "In the heuristic for C0201, how many conditionals to match", + "within a loop in before considering the loop a parser." + ], + "max_conditionals_custom_parser": 2, + "_help_min_statement_spacing": [ + "Require at least this many newlines between statements" + ], + "min_statement_spacing": 1, + "_help_max_statement_spacing": [ + "Require no more than this many newlines between statements" + ], + "max_statement_spacing": 2, + "max_returns": 6, + "max_branches": 12, + "max_arguments": 5, + "max_localvars": 15, + "max_statements": 50 + }, + "_help_encode": "Options affecting file encoding", + "encode": { + "_help_emit_byteorder_mark": [ + "If true, emit the unicode byte-order mark (BOM) at the start", + "of the file" + ], + "emit_byteorder_mark": false, + "_help_input_encoding": [ + "Specify the encoding of the input file. Defaults to utf-8" + ], + "input_encoding": "utf-8", + "_help_output_encoding": [ + "Specify the encoding of the output file. Defaults to utf-8.", + "Note that cmake only claims to support utf-8 so be careful", + "when using anything else" + ], + "output_encoding": "utf-8" + }, + "_help_misc": "Miscellaneous configurations options.", + "misc": { + "_help_per_command": [ + "A dictionary containing any per-command configuration", + "overrides. Currently only `command_case` is supported." + ], + "per_command": {} + } +} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..03f4e29 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,32 @@ +FROM ubuntu:noble + +ARG DEBIAN_FRONTEND="noninteractive" + +# Packages +COPY apt-packages.in /tmp/apt.in +RUN apt update && \ + grep -vE '^\s*#' /tmp/apt.in | xargs apt install --no-install-recommends -y && \ + rm -rf /var/lib/apt/lists/* && apt-get clean -y && rm /tmp/apt.in && \ + sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ + locale-gen + +# CPP config files +COPY .clang-format /.clang-format +COPY .clangd /.clangd +COPY .cmake-format.json /.cmake-format.json + +ENV TZ=Etc/UTC +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en + +RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +ARG CONTAINER_USER=developer +ARG WORKSPACE_FOLDER=/workspace +RUN useradd -s /bin/bash -m ${CONTAINER_USER} && \ + echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc && \ + mkdir ${WORKSPACE_FOLDER} && \ + chown ${CONTAINER_USER}:${CONTAINER_USER} ${WORKSPACE_FOLDER} + +USER ${CONTAINER_USER} diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in new file mode 100644 index 0000000..a330532 --- /dev/null +++ b/.devcontainer/apt-packages.in @@ -0,0 +1,14 @@ +ca-certificates +curl +locales +sudo +tzdata + +gcc +clang +clangd +make +cmake +clang-format +cmake-format +ninja-build diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ad5594b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,67 @@ +{ + "name": "CPP MilTech", + "build": { + "dockerfile": "Dockerfile", + "args": { + "WORKSPACE_FOLDER": "${localEnv:HOME}/${localWorkspaceFolderBasename}", + "CONTAINER_USER": "${localEnv:USER}" + } + }, + "remoteUser": "${localEnv:USER}", + "workspaceMount": "source=${localWorkspaceFolder},target=${localEnv:HOME}/${localWorkspaceFolderBasename},type=bind", + "workspaceFolder": "${localEnv:HOME}/${localWorkspaceFolderBasename}", + "mounts": [ + "source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,readonly,consistency=cached", + "source=${localEnv:HOME}/.devcontainers/${localWorkspaceFolderBasename}/.bash_history,target=/home/${localEnv:USER}/.bash_history,type=bind,consistency=delegated", + ], + "initializeCommand": [ + ".devcontainer/scripts/initialize" + ], + "runArgs": [ + "--network=host", + "--hostname=devcontainer", + "--add-host=devcontainer:127.0.1.1" + ], + "customizations": { + "vscode": { + "extensions": [ + "josetr.cmake-language-support-vscode", + "cheshirekow.cmake-format", + "ms-vscode.cpptools", + "llvm-vs-code-extensions.vscode-clangd" + ], + "settings": { + "clangd.path": "/usr/bin/clangd", + "clangd.arguments": [ + "--compile-commands-dir=ada_ws/build/x86_64/", + "--clang-tidy", + "--background-index" + ], + "clangd.restartAfterCrash": true, + "C_Cpp.intelliSenseEngine": "disabled", + "[cpp]": { + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", + "editor.quickSuggestions": { + "other": "on", + "comments": "off", + "strings": "off" + } + }, + "[cmake]": { + "editor.defaultFormatter": "cheshirekow.cmake-format" + }, + "cmakeFormat.args": [ + "--config-file=/.cmake-format.json" + ], + "cmakeFormat.exePath": "/usr/bin/cmake-format" + } + }, + "terminal.integrated.shell.linux": "bash", + "terminal.integrated.profiles.linux": { + "bash (container default)": { + "path": "/bin/bash", + "overrideName": true + } + } + } +} diff --git a/.devcontainer/scripts/initialize b/.devcontainer/scripts/initialize new file mode 100644 index 0000000..03347d2 --- /dev/null +++ b/.devcontainer/scripts/initialize @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# Create bash history file to persist the history +export DEVCONTAINER_DATA_ON_HOST=~/.devcontainers/`basename $PWD` +mkdir -p $DEVCONTAINER_DATA_ON_HOST +touch $DEVCONTAINER_DATA_ON_HOST/.bash_history diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd73274 --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# Build directories +build/ +build-*/ +out/ +bin/ +obj/ + +# IDE / editor +.vs/ +.vscode/ +.idea/ +*.swp +*.swo +*~ +.cache/ + +# OS +.DS_Store +Thumbs.db From 6851e1f0bbe908cbf8c461d513307a0fc8517b6e Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 16:35:29 +0200 Subject: [PATCH 03/49] fix: make devcontainer initialize script cross-platform - Harden .devcontainer/scripts/initialize for macOS, Linux, and WSL2 hosts by adding `set -euo pipefail`, quoting all variable expansions, switching backticks to $(...), and using ${HOME} instead of ~ inside assignments. - Mark the script executable in the Git index (100755) so hosts that don't carry POSIX mode bits still execute it in-container. - Add .gitattributes to pin LF line endings for shell scripts and the Dockerfile, preventing `bad interpreter: /usr/bin/env^M` failures when the repo is checked out on a Windows-backed filesystem (including the NTFS side of some WSL2 setups). - Drop .vscode/ from .gitignore so the repo can ship shared editor settings/launch configs that align with the dev container's clangd and cmake-format defaults. --- .devcontainer/scripts/initialize | 11 +++++++---- .gitattributes | 15 +++++++++++++++ .gitignore | 1 - 3 files changed, 22 insertions(+), 5 deletions(-) mode change 100644 => 100755 .devcontainer/scripts/initialize create mode 100644 .gitattributes diff --git a/.devcontainer/scripts/initialize b/.devcontainer/scripts/initialize old mode 100644 new mode 100755 index 03347d2..fbcd113 --- a/.devcontainer/scripts/initialize +++ b/.devcontainer/scripts/initialize @@ -1,6 +1,9 @@ #!/usr/bin/env bash +set -euo pipefail -# Create bash history file to persist the history -export DEVCONTAINER_DATA_ON_HOST=~/.devcontainers/`basename $PWD` -mkdir -p $DEVCONTAINER_DATA_ON_HOST -touch $DEVCONTAINER_DATA_ON_HOST/.bash_history +# Runs on the host (macOS, Linux, WSL2) before the container starts. +# Creates a bash history file under the host's home so shell history +# persists across container rebuilds. +DEVCONTAINER_DATA_ON_HOST="${HOME}/.devcontainers/$(basename "${PWD}")" +mkdir -p "${DEVCONTAINER_DATA_ON_HOST}" +touch "${DEVCONTAINER_DATA_ON_HOST}/.bash_history" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..795b063 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto eol=lf + +*.sh text eol=lf +.devcontainer/scripts/* text eol=lf +Dockerfile text eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary +*.tar binary +*.gz binary diff --git a/.gitignore b/.gitignore index cd73274..b27363a 100644 --- a/.gitignore +++ b/.gitignore @@ -68,7 +68,6 @@ obj/ # IDE / editor .vs/ -.vscode/ .idea/ *.swp *.swo From ef74c7888140b3f88c2b0b7679cbabb563b58f5b Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 16:48:20 +0200 Subject: [PATCH 04/49] fix: create nested WORKSPACE_FOLDER path in Dockerfile - Switch `mkdir` to `mkdir -p` in the final Dockerfile RUN so the container image build succeeds on macOS hosts, where WORKSPACE_FOLDER resolves to /Users// and /Users does not exist in the Ubuntu base image. Plain `mkdir` cannot create nested parents, which broke step 8/8 of the image build. - Switch `chown` to `chown -R` so ownership on the workspace dir (and any parents just created) is set for the container user, keeping behavior correct before the host workspace bind-mount is applied at runtime. --- .devcontainer/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 03f4e29..104d498 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -26,7 +26,7 @@ ARG CONTAINER_USER=developer ARG WORKSPACE_FOLDER=/workspace RUN useradd -s /bin/bash -m ${CONTAINER_USER} && \ echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc && \ - mkdir ${WORKSPACE_FOLDER} && \ - chown ${CONTAINER_USER}:${CONTAINER_USER} ${WORKSPACE_FOLDER} + mkdir -p ${WORKSPACE_FOLDER} && \ + chown -R ${CONTAINER_USER}:${CONTAINER_USER} ${WORKSPACE_FOLDER} USER ${CONTAINER_USER} From 5f8e4e7fbcf9845fc369c99104121fcb43ec5be0 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 16:49:10 +0200 Subject: [PATCH 05/49] chore: recommend Dev Containers extension via .vscode/extensions.json - Add .vscode/extensions.json recommending the `ms-vscode-remote. remote-containers` extension, the minimum required on the host VS Code to expose the "Reopen in Container" command. - Extension identifier is identical on macOS, Linux, and WSL2 hosts, so a single recommendation covers all supported dev platforms without branching per-OS. - Container-internal extensions (clangd, cpptools, cmake-format, cmake-language-support) remain declared in devcontainer.json's customizations.vscode.extensions and are intentionally not duplicated here, since they are installed inside the container rather than on the host. --- .vscode/extensions.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e6f2364 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode-remote.remote-containers" + ] +} From f8e41b06022c42a90ede303c1bbc2c329fffc159 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 16:53:47 +0200 Subject: [PATCH 06/49] chore: add macOS host prep script for Docker via Colima - Add preps/macos.sh that installs Colima, Docker CLI, and docker-buildx via Homebrew, links the buildx plugin into ~/.docker/cli-plugins so `docker buildx` is discoverable, and starts the Colima VM as the Docker runtime. - Scope kept to the minimum the Dockerfile-based dev container actually needs: no docker-compose (the devcontainer is not compose-based) and no preflight/smoke-test scaffolding. --- preps/macos.sh | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100755 preps/macos.sh diff --git a/preps/macos.sh b/preps/macos.sh new file mode 100755 index 0000000..5451a9b --- /dev/null +++ b/preps/macos.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +brew install colima docker docker-buildx + +mkdir -p ~/.docker/cli-plugins +ln -sfn "$(brew --prefix)/opt/docker-buildx/bin/docker-buildx" ~/.docker/cli-plugins/docker-buildx + +colima start From 93f889adf49bb86ecf408ce8e2b5eff85bdb6cb7 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Tue, 21 Apr 2026 17:57:11 +0200 Subject: [PATCH 07/49] chore: add Linux and Windows host prep scripts - Add preps/linux.sh that installs Docker Engine via the official get.docker.com convenience script and adds the current user to the docker group, the minimum needed for the Dev Container CLI to talk to dockerd without sudo after a relogin. - Add preps/windows.ps1 that runs `wsl --install -d Ubuntu` to set up WSL2 with the default Ubuntu distro. Docker Desktop and winget are intentionally avoided; Docker itself is installed inside WSL2 by running preps/linux.sh from the Ubuntu distro, and the script's header comment spells out that chain so users aren't left guessing where the Docker engine install happens. - Keep both scripts at the same minimum scope as preps/macos.sh: host-only setup, nothing more than what's required for VS Code Dev Containers to reach a working dockerd. --- preps/linux.sh | 5 +++++ preps/windows.ps1 | 3 +++ 2 files changed, 8 insertions(+) create mode 100755 preps/linux.sh create mode 100644 preps/windows.ps1 diff --git a/preps/linux.sh b/preps/linux.sh new file mode 100755 index 0000000..f70e35a --- /dev/null +++ b/preps/linux.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker "$USER" diff --git a/preps/windows.ps1 b/preps/windows.ps1 new file mode 100644 index 0000000..edf2ea9 --- /dev/null +++ b/preps/windows.ps1 @@ -0,0 +1,3 @@ +# Reboot after running. Then launch Ubuntu, set up your user, +# and run `bash preps/linux.sh` inside WSL to install Docker. +wsl --install -d Ubuntu From 49d7702b2ce9fff22c8915edf46468de3049fe70 Mon Sep 17 00:00:00 2001 From: Eugene Kuznetsov Date: Tue, 21 Apr 2026 20:23:55 +0200 Subject: [PATCH 08/49] lesson1 --- block2-lesson1/CMakeLists.txt | 9 +++++++++ block2-lesson1/main.cpp | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 block2-lesson1/CMakeLists.txt create mode 100644 block2-lesson1/main.cpp diff --git a/block2-lesson1/CMakeLists.txt b/block2-lesson1/CMakeLists.txt new file mode 100644 index 0000000..c0b5e96 --- /dev/null +++ b/block2-lesson1/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(section2 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_executable(app main.cpp) +target_compile_options(app PRIVATE -Wall -Wextra -pedantic) diff --git a/block2-lesson1/main.cpp b/block2-lesson1/main.cpp new file mode 100644 index 0000000..bc8f460 --- /dev/null +++ b/block2-lesson1/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "Hello, World!" << std::endl; + return 0; +} From ff09b4338c857db14ff55c07ab15da16150d0f45 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 00:22:30 +0200 Subject: [PATCH 09/49] docs: replace preps/ shell scripts with platform notes - Remove preps/linux.sh, preps/macos.sh, and preps/windows.ps1. The per-platform host setup is short enough to follow as plain prose and doesn't justify shipping runnable scripts that have to be kept in sync with the prose anyway. - Add preps/linux.md, preps/macos.md, and preps/windows.md with copy-pasteable commands for installing Docker (Engine on Linux, Colima on macOS, WSL2+Engine on Windows 11), including the non-obvious bits (BIOS virtualization on Windows, systemd in WSL, docker-group relogin) that a plain script would hide. - Add preps/README.md as an index that routes users by OS and by editor (VS Code Dev Containers vs. any other editor via the devcontainers CLI), plus the final verification flow. - Add preps/devcontainers-cli.md covering the @devcontainers/cli workflow so users on Neovim, JetBrains, Helix, Sublime, and others can drive the same container without VS Code. --- preps/README.md | 37 +++++++++++++++++++++++++ preps/devcontainers-cli.md | 52 +++++++++++++++++++++++++++++++++++ preps/linux.md | 23 ++++++++++++++++ preps/linux.sh | 5 ---- preps/macos.md | 37 +++++++++++++++++++++++++ preps/macos.sh | 9 ------- preps/windows.md | 55 ++++++++++++++++++++++++++++++++++++++ preps/windows.ps1 | 3 --- 8 files changed, 204 insertions(+), 17 deletions(-) create mode 100644 preps/README.md create mode 100644 preps/devcontainers-cli.md create mode 100644 preps/linux.md delete mode 100755 preps/linux.sh create mode 100644 preps/macos.md delete mode 100755 preps/macos.sh create mode 100644 preps/windows.md delete mode 100644 preps/windows.ps1 diff --git a/preps/README.md b/preps/README.md new file mode 100644 index 0000000..d43ce28 --- /dev/null +++ b/preps/README.md @@ -0,0 +1,37 @@ +# Preps + +Мінімум щоб devcontainer у цьому проєкті запрацював. + +По ОС: + +- `linux.md` +- `windows.md` +- `macos.md` + +По редактору: + +- VS Code - просто extension `Dev Containers`, далі `Reopen in Container`, нічого більше. +- Будь-який інший редактор (Neovim, JetBrains, Zed, Helix, Sublime, Emacs...) - див. `devcontainers-cli.md`. + +Перевірка після всього (з кореня проєкту). + +Через VS Code - `Reopen in Container`, далі у вбудованому терміналі (він уже всередині контейнера): + +```bash +cmake -S . -B build +cmake --build build +./build/app +``` + +Через CLI (без VS Code) - кожна команда через `devcontainer exec`: + +```bash +devcontainer up --workspace-folder . +devcontainer exec --workspace-folder . cmake -S . -B build +devcontainer exec --workspace-folder . cmake --build build +devcontainer exec --workspace-folder . ./build/app +``` + +Має вивести `Hello, section 2!`. + +Якщо щось не те - в чат курсу. diff --git a/preps/devcontainers-cli.md b/preps/devcontainers-cli.md new file mode 100644 index 0000000..1754da9 --- /dev/null +++ b/preps/devcontainers-cli.md @@ -0,0 +1,52 @@ +# Будь-який редактор замість VS Code + +Є офіційний CLI `@devcontainers/cli`, який робить те саме, що VS Code: читає `devcontainer.json`, будує образ, запускає контейнер, виконує команди всередині. + +Потрібен Node.js (16+): + +```bash +npm install -g @devcontainers/cli +``` + +Перевірка: + +```bash +devcontainer --version +``` + +## Базовий флоу + +В корені проєкту: + +```bash +devcontainer up --workspace-folder . # build + start +devcontainer exec --workspace-folder . bash # зайти в контейнер +``` + +Одна команда без входу в shell: + +```bash +devcontainer exec --workspace-folder . cmake -S . -B build +devcontainer exec --workspace-folder . cmake --build build +``` + +Після зміни `Dockerfile`: + +```bash +devcontainer up --workspace-folder . --remove-existing-container +``` + +## Як при цьому редагувати код + +Файли замаунчені на хост, тож редагування - будь-яким локальним редактором. Команди - через `devcontainer exec` в окремому терміналі. + +- **Neovim / Vim / Helix / Sublime / Emacs** - редагування на хості, команди через `devcontainer exec`. Простий і робочий шлях. +- **JetBrains (CLion, IntelliJ, Rider)** - має нативну підтримку: `File -> Remote Development -> Dev Containers`. Вони розуміють `devcontainer.json` напряму, без посередника. + +## Що втрачається порівняно з VS Code + +- Автоматичне встановлення extensions у контейнер (це специфіка VS Code). +- Інтегрований debug UI (свій редактор - свій debug UI, налаштовуй `gdb` сам). +- Automatic port forwarding - робиться через `forwardPorts` у `devcontainer.json` + ручне підключення. + +Для C++ workflow це все некритично: більшість роботи через shell. diff --git a/preps/linux.md b/preps/linux.md new file mode 100644 index 0000000..bd876a6 --- /dev/null +++ b/preps/linux.md @@ -0,0 +1,23 @@ +# Linux + +Docker Engine через офіційний скрипт: + +```bash +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker "$USER" +``` + +Вийти й зайти в сесію заново (або reboot) - нова група `docker` підхопиться. + +На Ubuntu/Debian/Fedora daemon стартує сам через systemd. Якщо ні: + +```bash +sudo systemctl start docker +sudo systemctl enable docker +``` + +Перевірка: + +```bash +docker run hello-world +``` diff --git a/preps/linux.sh b/preps/linux.sh deleted file mode 100755 index f70e35a..0000000 --- a/preps/linux.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -curl -fsSL https://get.docker.com | sh -sudo usermod -aG docker "$USER" diff --git a/preps/macos.md b/preps/macos.md new file mode 100644 index 0000000..b5402a3 --- /dev/null +++ b/preps/macos.md @@ -0,0 +1,37 @@ +# macOS + +Без Docker Desktop - через Colima. + +```bash +brew install colima docker docker-buildx +``` + +Підключити buildx як CLI plugin: + +```bash +mkdir -p ~/.docker/cli-plugins +ln -sfn "$(brew --prefix)/opt/docker-buildx/bin/docker-buildx" ~/.docker/cli-plugins/docker-buildx +``` + +Запустити: + +```bash +colima start +``` + +Перший запуск тягне linuxkit image - пару хвилин. + +Перевірка: + +```bash +docker run hello-world +docker buildx version +``` + +Автостарт при логіні (опційно): + +```bash +brew services start colima +``` + +На Apple Silicon (M1/M2/M3/M4) все запускається нативно як `arm64`. Якщо раптом попаде образ тільки `amd64` - `docker run --platform linux/amd64 ...` (повільніше, через qemu). diff --git a/preps/macos.sh b/preps/macos.sh deleted file mode 100755 index 5451a9b..0000000 --- a/preps/macos.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -brew install colima docker docker-buildx - -mkdir -p ~/.docker/cli-plugins -ln -sfn "$(brew --prefix)/opt/docker-buildx/bin/docker-buildx" ~/.docker/cli-plugins/docker-buildx - -colima start diff --git a/preps/windows.md b/preps/windows.md new file mode 100644 index 0000000..792976a --- /dev/null +++ b/preps/windows.md @@ -0,0 +1,55 @@ +# Windows 11 + +WSL2 з Ubuntu, всередині - Docker Engine. + +Перед встановленням - у BIOS/UEFI має бути увімкнена віртуалізація. Перевірка: Task Manager -> Performance -> CPU, внизу має бути `Virtualization: Enabled`. + +Якщо `Disabled` - reboot, зайти в BIOS/UEFI (F2 / Del / F10 при старті, залежить від вендора), знайти опцію типу `Intel Virtualization Technology` / `VT-x` / `AMD-V` / `SVM Mode` (зазвичай в секціях `Advanced`, `CPU Configuration` або `Security`), увімкнути, зберегти, вийти. + +В **admin PowerShell**: + +```powershell +wsl --install -d Ubuntu +``` + +Reboot. Після reboot Ubuntu запуститься - задати username + пароль. + +Перевірка (PowerShell): + +```powershell +wsl -l -v +``` + +Має бути `Ubuntu` / `Running` / `VERSION: 2`. Якщо `VERSION: 1` - `wsl --set-version Ubuntu 2`. + +Далі всередині Ubuntu: + +```bash +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker "$USER" +``` + +Вийти з WSL (`exit`), відкрити Ubuntu знов - підхопиться група `docker`. + +Daemon сам не стартує. Або руками: + +```bash +sudo service docker start +``` + +Або один раз увімкнути systemd - додати в `/etc/wsl.conf`: + +``` +[boot] +systemd=true +``` + +Потім `wsl --shutdown` з PowerShell і знов відкрити Ubuntu. + +Перевірка: + +```bash +docker run hello-world +``` + +**Важливо:** код тримати в `~/projects/...` всередині WSL, не на `C:\`. Через `/mnt/c/` IO в рази повільніше, VS Code буде нестерпно гальмувати. diff --git a/preps/windows.ps1 b/preps/windows.ps1 deleted file mode 100644 index edf2ea9..0000000 --- a/preps/windows.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -# Reboot after running. Then launch Ubuntu, set up your user, -# and run `bash preps/linux.sh` inside WSL to install Docker. -wsl --install -d Ubuntu From 44e8acb08c23e0d55ddd4535417d1f6fc0d92737 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 00:22:35 +0200 Subject: [PATCH 10/49] revert: remove block2-lesson1/ subdirectory - Delete block2-lesson1/CMakeLists.txt and block2-lesson1/main.cpp introduced by commit 49d7702 ("lesson1"). The repo is already scoped to a single lesson (tracked on the block-2-lesson-1 branch), so nesting source files under a same-named subdir is redundant and puts the cmake build/ directory one level deeper than necessary for no gain. --- block2-lesson1/CMakeLists.txt | 9 --------- block2-lesson1/main.cpp | 6 ------ 2 files changed, 15 deletions(-) delete mode 100644 block2-lesson1/CMakeLists.txt delete mode 100644 block2-lesson1/main.cpp diff --git a/block2-lesson1/CMakeLists.txt b/block2-lesson1/CMakeLists.txt deleted file mode 100644 index c0b5e96..0000000 --- a/block2-lesson1/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project(section2 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -add_executable(app main.cpp) -target_compile_options(app PRIVATE -Wall -Wextra -pedantic) diff --git a/block2-lesson1/main.cpp b/block2-lesson1/main.cpp deleted file mode 100644 index bc8f460..0000000 --- a/block2-lesson1/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - std::cout << "Hello, World!" << std::endl; - return 0; -} From df13fe589dd424df7d23abc4ac60f2a63fe5402c Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 00:22:43 +0200 Subject: [PATCH 11/49] feat: add lesson starter CMake project at repo root - Add CMakeLists.txt configuring a C++20 build of an `app` executable from main.cpp, with -Wall -Wextra -pedantic and CXX_EXTENSIONS=OFF so portability issues surface early. - Add main.cpp as the lesson's hello-world entry point, printing "Hello, section 2!" to stdout. - Add README.md covering prerequisites (the dev container under .devcontainer/ plus host setup in preps/), the cmake -S . -B build / cmake --build build flow, and the expected runtime output. --- CMakeLists.txt | 9 +++++++++ README.md | 32 ++++++++++++++++++++++++++++++++ main.cpp | 6 ++++++ 3 files changed, 47 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c0b5e96 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(section2 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_executable(app main.cpp) +target_compile_options(app PRIVATE -Wall -Wextra -pedantic) diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa32cb5 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Section 2 - стартовий код + +Мінімальний C++ проєкт, з якого стартує блок 2 курсу. + +## Передумови + +Працюємо всередині devcontainer (див. `../devcontainer_example/` або +стартові інструкції в `./preps/`). + +## Збірка + +```bash +cmake -S . -B build +cmake --build build +``` + +## Запуск + +```bash +./build/app +``` + +Очікуваний вивід: + +``` +Hello, section 2! +``` + +## Файли + +- `main.cpp` - програма. +- `CMakeLists.txt` - опис збірки для CMake. diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..002c916 --- /dev/null +++ b/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "Hello, section 2!\n"; + return 0; +} From 4e62365a2c4ce8f62a19b44ca7ccd2f220f295b1 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 14:48:38 +0200 Subject: [PATCH 12/49] docs(preps): rework clone flow and add Windows troubleshooting - README.md: linear numbered steps (Docker -> clone -> code . -> extension -> Reopen in Container) - OS files: clone uses --branch block-2-lesson-1; linux/windows include apt update + git install - windows.md: systemd=true as default so docker auto-starts on WSL boot - windows.md: troubleshooting for common Windows-native open mistakes (error 193, out-of-space in docker-desktop VM, missing WSL prefix, empty localEnv vars) - Impersonal tone throughout (no 2nd-person addressing) --- preps/README.md | 32 +++++++++++--------------------- preps/linux.md | 19 +++++++++++++++++++ preps/macos.md | 17 +++++++++++++++++ preps/windows.md | 43 +++++++++++++++++++++++++++++++++---------- 4 files changed, 80 insertions(+), 31 deletions(-) diff --git a/preps/README.md b/preps/README.md index d43ce28..5d93edc 100644 --- a/preps/README.md +++ b/preps/README.md @@ -2,20 +2,21 @@ Мінімум щоб devcontainer у цьому проєкті запрацював. -По ОС: +## Кроки -- `linux.md` -- `windows.md` -- `macos.md` +1. **Поставити Docker** - див. [linux.md](linux.md) / [windows.md](windows.md) / [macos.md](macos.md) для своєї ОС. +2. **Склонувати репо курсу** (гілка `block-2-lesson-1`). Команди в тому ж OS-файлі, секція "Як взяти код курсу". На Windows - клон йде **в WSL**, не на `C:\`. +3. **Відкрити у VS Code**: `code .` з Ubuntu-терміналу після клону (не з Windows Explorer - `devcontainer.json` цього проекту зламається без WSL-сесії). +4. **Встановити Dev Containers extension**: VS Code запропонує сам (із `.vscode/extensions.json`). Якщо ні - `Ctrl+Shift+X` (на macOS `Cmd+Shift+X`), пошук `Dev Containers`, Install. +5. **Reopen in Container**: VS Code запропонує - погодитись. Перший білд 3-5 хв. -По редактору: +## Альтернативні редактори -- VS Code - просто extension `Dev Containers`, далі `Reopen in Container`, нічого більше. -- Будь-який інший редактор (Neovim, JetBrains, Zed, Helix, Sublime, Emacs...) - див. `devcontainers-cli.md`. +Не VS Code? Див. [devcontainers-cli.md](devcontainers-cli.md) - Neovim, JetBrains, Zed, Helix, Sublime, Emacs. -Перевірка після всього (з кореня проєкту). +## Перевірка -Через VS Code - `Reopen in Container`, далі у вбудованому терміналі (він уже всередині контейнера): +Всередині контейнера (VS Code terminal): ```bash cmake -S . -B build @@ -23,15 +24,4 @@ cmake --build build ./build/app ``` -Через CLI (без VS Code) - кожна команда через `devcontainer exec`: - -```bash -devcontainer up --workspace-folder . -devcontainer exec --workspace-folder . cmake -S . -B build -devcontainer exec --workspace-folder . cmake --build build -devcontainer exec --workspace-folder . ./build/app -``` - -Має вивести `Hello, section 2!`. - -Якщо щось не те - в чат курсу. +Має вивести `Hello, section 2!`. Якщо не працює - в чат курсу. diff --git a/preps/linux.md b/preps/linux.md index bd876a6..6ee61f6 100644 --- a/preps/linux.md +++ b/preps/linux.md @@ -21,3 +21,22 @@ sudo systemctl enable docker ```bash docker run hello-world ``` + +## Як взяти код курсу + +```bash +sudo apt update && sudo apt install -y git # або еквівалент вашого пакетного менеджера + +git config --global user.name "Your Name" +git config --global user.email "you@example.com" + +mkdir -p ~/projects +cd ~/projects +git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git +cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +code . +``` + +VS Code запропонує встановити Dev Containers extension (із `.vscode/extensions.json`) - погодитись. Якщо промпт не зявився: `Ctrl+Shift+X`, пошук `Dev Containers`, Install. + +Далі VS Code запропонує `Reopen in Container` - теж погодитись. diff --git a/preps/macos.md b/preps/macos.md index b5402a3..e6a11ff 100644 --- a/preps/macos.md +++ b/preps/macos.md @@ -35,3 +35,20 @@ brew services start colima ``` На Apple Silicon (M1/M2/M3/M4) все запускається нативно як `arm64`. Якщо раптом попаде образ тільки `amd64` - `docker run --platform linux/amd64 ...` (повільніше, через qemu). + +## Як взяти код курсу + +```bash +git config --global user.name "Your Name" +git config --global user.email "you@example.com" + +mkdir -p ~/projects +cd ~/projects +git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git +cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +code . +``` + +VS Code запропонує встановити Dev Containers extension (із `.vscode/extensions.json`) - погодитись. Якщо промпт не зявився: `Cmd+Shift+X`, пошук `Dev Containers`, Install. + +Далі VS Code запропонує `Reopen in Container` - теж погодитись. diff --git a/preps/windows.md b/preps/windows.md index 792976a..b1dcf63 100644 --- a/preps/windows.md +++ b/preps/windows.md @@ -29,22 +29,14 @@ curl -fsSL https://get.docker.com | sh sudo usermod -aG docker "$USER" ``` -Вийти з WSL (`exit`), відкрити Ubuntu знов - підхопиться група `docker`. - -Daemon сам не стартує. Або руками: - -```bash -sudo service docker start -``` - -Або один раз увімкнути systemd - додати в `/etc/wsl.conf`: +Щоб daemon стартував автоматично при кожному запуску WSL, увімкнути systemd - додати в `/etc/wsl.conf`: ``` [boot] systemd=true ``` -Потім `wsl --shutdown` з PowerShell і знов відкрити Ubuntu. +З PowerShell: `wsl --shutdown`. Знов відкрити Ubuntu - це одночасно підхопить нову групу `docker` і стартоне daemon. Перевірка: @@ -53,3 +45,34 @@ docker run hello-world ``` **Важливо:** код тримати в `~/projects/...` всередині WSL, не на `C:\`. Через `/mnt/c/` IO в рази повільніше, VS Code буде нестерпно гальмувати. + +## Як взяти код курсу + +Клон йде в WSL (не на `C:\`). В Ubuntu-терміналі: + +```bash +sudo apt update && sudo apt install -y git + +git config --global user.name "Your Name" +git config --global user.email "you@example.com" + +mkdir -p ~/projects +cd ~/projects +git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git +cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +code . +``` + +VS Code запропонує встановити Dev Containers extension (із `.vscode/extensions.json`) - погодитись. Якщо промпт не зявився: `Ctrl+Shift+X`, пошук `Dev Containers`, Install. + +Далі VS Code запропонує `Reopen in Container` - теж погодитись. + +## Якщо щось не те + +`Cannot create process, error code: 193` - проект відкритий з Windows, а не з WSL. `code .` треба робити з Ubuntu-терміналу, не з Windows Explorer. + +`No space left on device` у шляху `/root/...` - встановлена тільки службова WSL-дистрибуція `docker-desktop`, без окремої Ubuntu. Це by design, в неї не можна ставити VS Code Server. Фікс: `wsl --install -d Ubuntu` з admin PowerShell, потім клонувати в нову Ubuntu. + +Внизу ліворуч у VS Code нема префіксу `WSL: Ubuntu` - сесія VS Code не в WSL. Закрити, зайти в Ubuntu через `wsl`, `cd` у папку проекту, `code .` звідти. + +`${localEnv:HOME}` / `${localEnv:USER}` у логах Dev Containers - той самий симптом: на Windows ці змінні порожні, треба відкривати з WSL де вони визначені. From d92dca4fb60670a28d9a8f767dd6bd6c5146817f Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 15:05:48 +0200 Subject: [PATCH 13/49] fix(devcontainer): create ~/.ssh in initialize to prevent mount failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On fresh machines (students who never generated SSH keys) the bind-mount source=${HOME}/.ssh failed with "bind source path does not exist", blocking container start. - scripts/initialize: create ~/.ssh with 700 perms if missing (idempotent, does not touch existing .ssh) - preps/README.md: add "Якщо вже клонували репо раніше" section with git pull steps for students who cloned before this fix --- .devcontainer/scripts/initialize | 4 ++++ preps/README.md | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/.devcontainer/scripts/initialize b/.devcontainer/scripts/initialize index fbcd113..20a1d63 100755 --- a/.devcontainer/scripts/initialize +++ b/.devcontainer/scripts/initialize @@ -7,3 +7,7 @@ set -euo pipefail DEVCONTAINER_DATA_ON_HOST="${HOME}/.devcontainers/$(basename "${PWD}")" mkdir -p "${DEVCONTAINER_DATA_ON_HOST}" touch "${DEVCONTAINER_DATA_ON_HOST}/.bash_history" + +# Ensure ~/.ssh exists so the bind-mount in devcontainer.json does not fail +# on fresh machines where the user never generated SSH keys yet. +[ -d "${HOME}/.ssh" ] || mkdir -m 700 "${HOME}/.ssh" diff --git a/preps/README.md b/preps/README.md index 5d93edc..29323c6 100644 --- a/preps/README.md +++ b/preps/README.md @@ -10,6 +10,19 @@ 4. **Встановити Dev Containers extension**: VS Code запропонує сам (із `.vscode/extensions.json`). Якщо ні - `Ctrl+Shift+X` (на macOS `Cmd+Shift+X`), пошук `Dev Containers`, Install. 5. **Reopen in Container**: VS Code запропонує - погодитись. Перший білд 3-5 хв. +## Якщо вже клонували репо раніше + +Якщо репо клоновано до нових оновлень (наприклад фіксів для devcontainer) - підтягнути зміни з remote. В Ubuntu/shell-терміналі: + +```bash +cd ~/projects/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +git pull +``` + +Якщо `cd` каже "No such file or directory" - перевірити куди було клоновано раніше (за інструкцією мало бути `~/projects/`, але могло бути інакше). + +Потім у VS Code знов `Reopen in Container`. + ## Альтернативні редактори Не VS Code? Див. [devcontainers-cli.md](devcontainers-cli.md) - Neovim, JetBrains, Zed, Helix, Sublime, Emacs. From 8859f966c90193376bc5829a9a6953204eff6bc8 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Wed, 22 Apr 2026 18:26:14 +0200 Subject: [PATCH 14/49] fix(devcontainer): free UID 1000 by removing default ubuntu user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ubuntu 24.04 (Noble) base image ships with a pre-created 'ubuntu' user at UID 1000. Without this fix, useradd for the course user picked UID 1001 (next available), so bind-mounted workspace files (owned by host UID 1000) appeared as owned by 'ubuntu' inside the container. Dev Containers CLI updateUID step also could not realign the course user to 1000 because UID 1000 was already taken. - Dockerfile: userdel -r ubuntu + groupdel ubuntu before useradd, then useradd -u 1000 -U so the course user owns UID 1000 and bind-mount ownership maps to the right name inside the container. - preps/README.md: in "Якщо вже клонували" section, switch instruction from "Reopen in Container" to "Rebuild Container" so students pick up the new Dockerfile layer after git pull. --- .devcontainer/Dockerfile | 9 +++++---- preps/README.md | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 104d498..fec6a13 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -24,9 +24,10 @@ RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers ARG CONTAINER_USER=developer ARG WORKSPACE_FOLDER=/workspace -RUN useradd -s /bin/bash -m ${CONTAINER_USER} && \ - echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc && \ - mkdir -p ${WORKSPACE_FOLDER} && \ - chown -R ${CONTAINER_USER}:${CONTAINER_USER} ${WORKSPACE_FOLDER} + +RUN userdel -r ubuntu 2>/dev/null || true && \ + groupdel ubuntu 2>/dev/null || true && \ + useradd -s /bin/bash -m -u 1000 -U ${CONTAINER_USER} && \ + echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc USER ${CONTAINER_USER} diff --git a/preps/README.md b/preps/README.md index 29323c6..573c3c6 100644 --- a/preps/README.md +++ b/preps/README.md @@ -12,7 +12,7 @@ ## Якщо вже клонували репо раніше -Якщо репо клоновано до нових оновлень (наприклад фіксів для devcontainer) - підтягнути зміни з remote. В Ubuntu/shell-терміналі: +Якщо репо клоновано до нових оновлень (фікси для devcontainer) - підтягнути зміни. В Ubuntu/shell-терміналі: ```bash cd ~/projects/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY @@ -21,7 +21,7 @@ git pull Якщо `cd` каже "No such file or directory" - перевірити куди було клоновано раніше (за інструкцією мало бути `~/projects/`, але могло бути інакше). -Потім у VS Code знов `Reopen in Container`. +Потім у VS Code: Command Palette (Ctrl+Shift+P, на macOS Cmd+Shift+P) -> `Dev Containers: Rebuild Container`. Саме Rebuild, не Reopen - треба перебудувати образ, інакше фікси в Dockerfile не підхопляться. ## Альтернативні редактори From f5dd838b6bce4c85de03ebb0464549a1ae114b51 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 23 Apr 2026 15:18:00 +0200 Subject: [PATCH 15/49] feat(homework-04): add wheel odometry starter (CMake, main stub, samples) --- homework_04/CMakeLists.txt | 9 ++++++++ homework_04/data/combined.txt | 41 +++++++++++++++++++++++++++++++++++ homework_04/data/straight.txt | 11 ++++++++++ homework_04/data/turn.txt | 11 ++++++++++ homework_04/src/main.cpp | 22 +++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 homework_04/CMakeLists.txt create mode 100644 homework_04/data/combined.txt create mode 100644 homework_04/data/straight.txt create mode 100644 homework_04/data/turn.txt create mode 100644 homework_04/src/main.cpp diff --git a/homework_04/CMakeLists.txt b/homework_04/CMakeLists.txt new file mode 100644 index 0000000..b7c94c2 --- /dev/null +++ b/homework_04/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(ugv_odometry CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_executable(ugv_odometry src/main.cpp) +target_compile_options(ugv_odometry PRIVATE -Wall -Wextra -pedantic) diff --git a/homework_04/data/combined.txt b/homework_04/data/combined.txt new file mode 100644 index 0000000..6346531 --- /dev/null +++ b/homework_04/data/combined.txt @@ -0,0 +1,41 @@ +0 0 0 0 0 +1000 1359 1359 1359 1359 +2000 2718 2718 2718 2718 +3000 4077 4077 4077 4077 +4000 5436 5436 5436 5436 +5000 6795 6795 6795 6795 +6000 8154 8154 8154 8154 +7000 9513 9513 9513 9513 +8000 10872 10872 10872 10872 +9000 12231 12231 12231 12231 +10000 13590 13590 13590 13590 +11000 14949 14949 14949 14949 +12000 16308 16308 16308 16308 +13000 17667 17667 17667 17667 +14000 19026 19026 19026 19026 +15000 20385 20385 20385 20385 +16000 21744 21744 21744 21744 +17000 23103 23103 23103 23103 +18000 24462 24462 24462 24462 +19000 25821 25821 25821 25821 +20000 27180 27180 27180 27180 +21000 27430 27473 27430 27473 +22000 27680 27766 27680 27766 +23000 27930 28059 27930 28059 +24000 28180 28352 28180 28352 +25000 28430 28645 28430 28645 +26000 28680 28938 28680 28938 +27000 28930 29231 28930 29231 +28000 29180 29524 29180 29524 +29000 29430 29817 29430 29817 +30000 29680 30110 29680 30110 +31000 29930 30403 29930 30403 +32000 30180 30696 30180 30696 +33000 30430 30989 30430 30989 +34000 30680 31282 30680 31282 +35000 30930 31575 30930 31575 +36000 31180 31868 31180 31868 +37000 31430 32161 31430 32161 +38000 31680 32454 31680 32454 +39000 31930 32747 31930 32747 +40000 32180 33040 32180 33040 diff --git a/homework_04/data/straight.txt b/homework_04/data/straight.txt new file mode 100644 index 0000000..fab65f1 --- /dev/null +++ b/homework_04/data/straight.txt @@ -0,0 +1,11 @@ +0 0 0 0 0 +1000 543 543 543 543 +2000 1086 1086 1086 1086 +3000 1629 1629 1629 1629 +4000 2172 2172 2172 2172 +5000 2715 2715 2715 2715 +6000 3258 3258 3258 3258 +7000 3801 3801 3801 3801 +8000 4344 4344 4344 4344 +9000 4887 4887 4887 4887 +10000 5430 5430 5430 5430 diff --git a/homework_04/data/turn.txt b/homework_04/data/turn.txt new file mode 100644 index 0000000..1299b92 --- /dev/null +++ b/homework_04/data/turn.txt @@ -0,0 +1,11 @@ +0 0 0 0 0 +1000 229 314 229 314 +2000 458 628 458 628 +3000 687 942 687 942 +4000 916 1256 916 1256 +5000 1145 1570 1145 1570 +6000 1374 1884 1374 1884 +7000 1603 2198 1603 2198 +8000 1832 2512 1832 2512 +9000 2061 2826 2061 2826 +10000 2290 3140 2290 3140 diff --git a/homework_04/src/main.cpp b/homework_04/src/main.cpp new file mode 100644 index 0000000..42894b6 --- /dev/null +++ b/homework_04/src/main.cpp @@ -0,0 +1,22 @@ +#include + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "usage: ugv_odometry \n"; + return 1; + } + + // TODO: implement wheel odometry for a 4-wheel differential-drive UGV. + // + // Parameters: + // ticks_per_revolution = 1024 + // wheel_radius_m = 0.3 + // wheelbase_m = 1.0 + // + // Input: text file with 5 whitespace-separated numbers per line: + // timestamp_ms fl_ticks fr_ticks bl_ticks br_ticks + // Output: same tabular format on stdout, starting from the second sample: + // timestamp_ms x y theta + + return 0; +} From 50a58c33a2c10fcd2195cf039011c81650807dc0 Mon Sep 17 00:00:00 2001 From: Eugene Kuznetsov Date: Thu, 23 Apr 2026 20:45:22 +0000 Subject: [PATCH 16/49] feat(devcontainer): add ssh and git packages --- .devcontainer/apt-packages.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index a330532..91a0a3a 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -3,6 +3,8 @@ curl locales sudo tzdata +ssh +git gcc clang From 26b4e1637758aab5aa4dee9c645dda1b8ce8cee8 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:09:40 +0200 Subject: [PATCH 17/49] chore(devcontainer): pin ubuntu base image by digest The :noble tag is mutable on Docker Hub, so the same Dockerfile can pull different images over time. Pin to a digest for reproducibility. --- .devcontainer/Dockerfile | 62 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fec6a13..3ae45b1 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,8 +1,32 @@ -FROM ubuntu:noble +# syntax=docker/dockerfile:1 +# Ubuntu 24.04 LTS (noble), закріплено по SHA256 digest. +# +# Теги на Docker Hub змінюються: :noble перевипускається кілька разів +# на рік з оновленнями безпеки. Той самий Dockerfile з тим самим тегом +# завтра може витягнути інший образ. Пін по digest робить так, щоб +# через будь-який час збірка давала ту саму базу. +# +# Оновити digest (коли планово бампаємо базу): +# docker pull ubuntu:noble +# docker inspect --format='{{index .RepoDigests 0}}' ubuntu:noble +FROM ubuntu:noble@sha256:c4a8d5503dfb2a3eb8ab5f807da5bc69a85730fb49b5cfca2330194ebcc41c7b + +# Noninteractive frontend, щоб apt не зависнув чекаючи відповіді на +# питання (часовий пояс, клавіатура, kernel upgrade prompt). У Dockerfile +# інтерактивного stdin немає - без цього build може зависнути назавжди. ARG DEBIAN_FRONTEND="noninteractive" -# Packages +# Пакети зі списку apt-packages.in (по одному на рядок, коментарі через #). +# +# Прапори й чистка: +# - --no-install-recommends пропускає необов'язкові залежні пакети +# (документацію, десктопні інтеграції). Економить 200-500 MB і час. +# - rm /var/lib/apt/lists/* + apt-get clean чистять кеш apt після +# install. Без цього layer тягне сотні MB зайвого. +# - locale-gen генерує en_US.UTF-8 щоб C++ std::cout коректно виводив +# non-ASCII (кирилиця, UTF-8 JSON). Без цього setlocale повертає NULL +# і stdout/stderr ламаються на кирилиці. COPY apt-packages.in /tmp/apt.in RUN apt update && \ grep -vE '^\s*#' /tmp/apt.in | xargs apt install --no-install-recommends -y && \ @@ -10,24 +34,56 @@ RUN apt update && \ sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ locale-gen -# CPP config files +# Конфіги C++ тулів кладемо в корінь /, не в home. clangd, clang-format, +# cmake-format шукають такі файли в батьківських директоріях від точки +# запуску - тобто вони знайдуться незалежно від того, де всередині +# контейнера змонтовано проєкт. COPY .clang-format /.clang-format COPY .clangd /.clangd COPY .cmake-format.json /.cmake-format.json +# Environment: +# - TZ: UTC як нейтральний дефолт. Логи завжди в UTC, без суперечок з +# літнім часом різних регіонів. +# - LC_ALL / LANG / LANGUAGE: en_US.UTF-8 щоб stdout/stderr коректно +# виводили UTF-8 текст (кирилиця, emoji, JSON з non-ASCII ключами). ENV TZ=Etc/UTC ENV LC_ALL=en_US.UTF-8 ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en +# Passwordless sudo для dev-контейнера: зручно для apt install на ходу, +# debug tools, etc. У прод-образах ніколи - там контейнер має жорстко +# визначений набір прав. RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers +# CONTAINER_USER і WORKSPACE_FOLDER приходять з devcontainer.json: +# CONTAINER_USER <- ${localEnv:USER} +# WORKSPACE_FOLDER <- ${localEnv:HOME}/${localWorkspaceFolderBasename} +# Локально VS Code підставляє host username і path. У CI +# devcontainers/ci підставляє `runner` і `/home/runner/`. ARG CONTAINER_USER=developer ARG WORKSPACE_FOLDER=/workspace +# Ubuntu noble постачається з дефолтним користувачем ubuntu на UID 1000. +# Видаляємо його, щоб звільнити UID 1000 для CONTAINER_USER. +# +# Чому саме UID 1000: на більшості Linux-машин перший користувач отримує +# UID 1000. Збіг UID у контейнера і на хості дає правильний ownership +# bind-mounted workspace-а. Без цього файли створені в контейнері +# виглядають з хоста як nobody і навпаки. +# +# `2>/dev/null || true` щоб build не впав, якщо ubuntu вже нема (інший +# base image або майбутній noble може прийти без дефолтного user-а). +# +# PROMPT_COMMAND з history -a; history -n скидає історію bash у файл +# після кожної команди. У devcontainer.json цей файл змонтований з +# хоста - тому історія переживає перезапуск контейнера. RUN userdel -r ubuntu 2>/dev/null || true && \ groupdel ubuntu 2>/dev/null || true && \ useradd -s /bin/bash -m -u 1000 -U ${CONTAINER_USER} && \ echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc +# Всі подальші команди у контейнері (включно з процесами VS Code remote +# extension) виконуються від CONTAINER_USER, не від root. USER ${CONTAINER_USER} From 5f6fb294c82b1f0bac2a8c135d0487445f233ca1 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:09:47 +0200 Subject: [PATCH 18/49] build(cmake): include homework_04 from root So `cmake --build build` at the repo root builds both the root app and the homework target in one pass. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0b5e96..d710633 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,3 +7,6 @@ set(CMAKE_CXX_EXTENSIONS OFF) add_executable(app main.cpp) target_compile_options(app PRIVATE -Wall -Wextra -pedantic) + +# Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. +add_subdirectory(homework_04) From c84052763522e9464df87decef3f154c7b9ba0a2 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:10:12 +0200 Subject: [PATCH 19/49] ci(build): add workflow using devcontainers/ci Runs cmake configure+build inside the repo's devcontainer so CI matches the local VS Code environment. --- .github/workflows/build.yml | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..22b1b10 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,47 @@ +# GitHub Actions workflow: збірка проєкту через репівський devcontainer. +# +# Ключова ідея: CI використовує ТОЙ САМИЙ Dockerfile що й локально у +# VS Code. Інакше "works on my machine" виникає постійно: локальний +# devcontainer має одні версії тулів, CI runner інші. Тут вони однакові. + +name: Build + +# Тригери: +# - push: запускаємо на кожен push у будь-яку гілку. +# - pull_request: запускаємо при відкритті PR і кожному update гілки. +on: + push: + pull_request: + +jobs: + build: + # ubuntu-latest - загальний GitHub runner з preinstalled Docker і + # актуальною Ubuntu. Для нашого випадку (build всередині + # devcontainer-а) специфічний runner не потрібен. + runs-on: ubuntu-latest + + steps: + # Стандартний checkout коду. v4 - поточна стабільна major. + - name: Checkout + uses: actions/checkout@v4 + + # devcontainers/ci - офіційний action від Microsoft, який запускає + # build всередині devcontainer-а. Послідовність: + # + # 1. Читає .devcontainer/devcontainer.json. + # 2. Підставляє ${localEnv:USER} і ${localEnv:HOME} зі змінних + # середовища runner-а (на ubuntu-latest це `runner` і + # `/home/runner`). + # 3. Будує Docker image з Dockerfile, передаючи підставлені args. + # 4. Монтує checkout у workspaceFolder. + # 5. Виконує runCmd від імені remoteUser. + # + # -G Ninja: явно обираємо Ninja-генератор. ninja-build уже є у + # devcontainer-і, швидший за Make на проєктах з багатьма файлами + # (кращий паралелізм, менше overhead-у на запуск shell-команд). + - name: Build inside dev container + uses: devcontainers/ci@v0.3 + with: + runCmd: | + cmake -S . -B build -G Ninja + cmake --build build From 4de1dcd067fc13da3daa0e0c1d549b15a06afa3e Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:10:28 +0200 Subject: [PATCH 20/49] ci(build): disable image push in devcontainers/ci Default push=filter tries to push the built image and fails with 'imageName is required' because no registry is configured. The image is only needed locally on the runner for the build step. --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22b1b10..d77258c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,11 @@ jobs: - name: Build inside dev container uses: devcontainers/ci@v0.3 with: + # push: never - не публікувати built image ніде. За замовчуванням + # action пробує push у registry і падає з "imageName is required" + # бо ми не конфігуруємо registry. Image потрібен тільки локально + # на runner-і для runCmd. + push: never runCmd: | cmake -S . -B build -G Ninja cmake --build build From 56291464bfef49a8884ddfafeb248f67a26c6c53 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:10:41 +0200 Subject: [PATCH 21/49] ci(build): limit push trigger to main to avoid duplicate PR runs A push to a feature branch with an open PR used to trigger the workflow twice (once from push, once from pull_request synchronize). Restricting push to main keeps push runs for merges and leaves PR runs to the pull_request trigger. --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d77258c..9bb8d39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,10 +7,13 @@ name: Build # Тригери: -# - push: запускаємо на кожен push у будь-яку гілку. +# - push.branches: [main] - запускаємо тільки при push у main (тобто +# при merge PR). Обмеження важливе: без нього push + pull_request +# запускали б одну й ту саму збірку двічі на кожен update PR. # - pull_request: запускаємо при відкритті PR і кожному update гілки. on: push: + branches: [main] pull_request: jobs: From 0f42450fbf236a87d8036d4ba9d1dd31b80f0dc1 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:28:41 +0200 Subject: [PATCH 22/49] chore(devcontainer): drop hardcoded UID 1000, rely on updateRemoteUserUID useradd previously hardcoded -u 1000 to match a common host UID. That is what VS Code and devcontainers/ci already handle at container start via updateRemoteUserUID, which syncs the container user UID to the host's. All we need from the image is that UID 1000 be free so the default useradd slot and the runtime remap don't collide with the ubuntu user that noble ships on UID 1000. Remove ubuntu conditionally (guarded by `id ubuntu`) and let useradd pick the UID on its own. --- .devcontainer/Dockerfile | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3ae45b1..4c3ba82 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -65,23 +65,32 @@ RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers ARG CONTAINER_USER=developer ARG WORKSPACE_FOLDER=/workspace -# Ubuntu noble постачається з дефолтним користувачем ubuntu на UID 1000. -# Видаляємо його, щоб звільнити UID 1000 для CONTAINER_USER. +# Noble base постачається з дефолтним ubuntu на UID 1000. Видаляємо +# його до створення CONTAINER_USER, інакше remap UID ламається на WSL +# та будь-якому хості, де host user має UID 1000. # -# Чому саме UID 1000: на більшості Linux-машин перший користувач отримує -# UID 1000. Збіг UID у контейнера і на хості дає правильний ownership -# bind-mounted workspace-а. Без цього файли створені в контейнері -# виглядають з хоста як nobody і навпаки. +# Чому: VS Code і devcontainers/ci на старті контейнера підлаштовують +# UID нашого користувача під host через механізм updateRemoteUserUID. +# Він відмовляється перепризначати UID, якщо цільовий уже зайнятий +# кимось у /etc/passwd - просто друкує "User with UID exists" і +# пропускає remap. На WSL (host UID = 1000) цільовий remap-а це 1000, +# і коли ubuntu@1000 на місці, remap скіпається, наш CONTAINER_USER +# лишається з UID 1001, а ownership bind-mount не збігається з host - +# файли створені в контейнері з хоста виглядають як чужі. # -# `2>/dev/null || true` щоб build не впав, якщо ubuntu вже нема (інший -# base image або майбутній noble може прийти без дефолтного user-а). +# Звільнивши слот 1000, дозволяємо useradd без -u взяти його. Далі +# remap або робить no-op (host UID = 1000), або переносить наш UID +# на інший вільний слот (host UID != 1000). +# +# `if id ubuntu` - щоб майбутній noble без дефолтного ubuntu не ламав build. # # PROMPT_COMMAND з history -a; history -n скидає історію bash у файл # після кожної команди. У devcontainer.json цей файл змонтований з # хоста - тому історія переживає перезапуск контейнера. -RUN userdel -r ubuntu 2>/dev/null || true && \ - groupdel ubuntu 2>/dev/null || true && \ - useradd -s /bin/bash -m -u 1000 -U ${CONTAINER_USER} && \ +RUN if id ubuntu >/dev/null 2>&1; then \ + userdel -r ubuntu && groupdel ubuntu 2>/dev/null || true; \ + fi && \ + useradd -s /bin/bash -m ${CONTAINER_USER} && \ echo "\nexport PROMPT_COMMAND=\"history -a; history -n\n\"" >> /home/${CONTAINER_USER}/.bashrc # Всі подальші команди у контейнері (включно з процесами VS Code remote From 2b28f49bf142af3f1c7182ff8157d67f5b7a13de Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:39:42 +0200 Subject: [PATCH 23/49] build(cmake): drop root main.cpp and app target Root CMakeLists.txt used to build a small hello app from a top-level main.cpp. With homework_NN subdirectories in place (each with its own CMakeLists.txt and executable target), the root app is no longer useful. Remove main.cpp and the add_executable(app) line from the root CMakeLists.txt. --- CMakeLists.txt | 3 --- main.cpp | 6 ------ 2 files changed, 9 deletions(-) delete mode 100644 main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d710633..4025f7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,5 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -add_executable(app main.cpp) -target_compile_options(app PRIVATE -Wall -Wextra -pedantic) - # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 002c916..0000000 --- a/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - std::cout << "Hello, section 2!\n"; - return 0; -} From bf3f0b6165e0ca054137b3d1a6e49f4bee70ad09 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:39:48 +0200 Subject: [PATCH 24/49] docs(readme): rewrite root README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the "Section 2 - стартовий код" stub with a course-level README covering clone + Reopen in Container + build, repo structure, how to add new homework_NN, and what CI does. Platform-specific setup stays in preps/. --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index fa32cb5..ba28047 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,72 @@ -# Section 2 - стартовий код +# C++ для військових технологій -Мінімальний C++ проєкт, з якого стартує блок 2 курсу. +Навчальний репозиторій курсу. Містить dev-контейнер з C++ toolchain, стартову +структуру проєкту і окремі папки під кожну домашню роботу. -## Передумови +## Швидкий старт -Працюємо всередині devcontainer (див. `../devcontainer_example/` або -стартові інструкції в `./preps/`). +1. Склонувати репо і зайти в папку: + ```bash + git clone https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git + cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY + ``` +2. Відкрити у VS Code: + ```bash + code . + ``` + VS Code запропонує `Reopen in Container` - погодитись. Перший запуск + тягне і білдить Docker image (5-10 хв), наступні запуски миттєві. -## Збірка +3. Всередині контейнера зібрати весь репо: + ```bash + cmake -S . -B build -G Ninja + cmake --build build + ``` + Виконувані файли домашніх робіт з'являться в `build/homework_XX/`. -```bash -cmake -S . -B build -cmake --build build -``` +## Перед стартом -## Запуск +Потрібно: +- **Docker** - Docker Desktop на Windows/macOS, Docker Engine на Linux. +- **VS Code** з розширенням `ms-vscode-remote.remote-containers` (Dev Containers). -```bash -./build/app -``` +Покрокові інструкції під конкретну платформу: +- [Windows 11 + WSL2](preps/windows.md) +- [Linux](preps/linux.md) +- [macOS](preps/macos.md) + +Загальний огляд інструментів в preps/ - у [preps/README.md](preps/README.md). -Очікуваний вивід: +## Структура репо ``` -Hello, section 2! +. +├── .devcontainer/ # Docker image + конфіг dev-контейнера +├── .github/workflows/ # GitHub Actions: CI build через devcontainer +├── CMakeLists.txt # корневий CMakeLists, підтягує homework_XX через add_subdirectory +├── homework_XX/ # окрема домашка, кожна зі своїм CMakeLists.txt +└── preps/ # інструкції зі сетапу за платформами ``` -## Файли +## Як додати нову домашку + +1. Створити папку `homework_NN/` з власним `CMakeLists.txt`, `src/`, `include/` + і даними якщо є. +2. Додати у корневий CMakeLists.txt рядок: + ```cmake + add_subdirectory(homework_NN) + ``` +3. Відкрити PR. GitHub Actions перевірить що все збирається у devcontainer-і. + +## Що робить CI + +Workflow `.github/workflows/build.yml` запускається на кожен PR (і на push у +main). Він: +1. Будує devcontainer image з Dockerfile. +2. Виконує всередині `cmake -S . -B build -G Ninja && cmake --build build`. +3. Падає якщо хоч одна `homework_XX` не компілюється з + тулчейном з devcontainer-а (gcc-13, clang-18, C++20). -- `main.cpp` - програма. -- `CMakeLists.txt` - опис збірки для CMake. +Якщо CI червоний локально зібралося, але на CI ні - швидше за все +devcontainer треба rebuild локально (Dev Containers: Rebuild Container), +щоб підхопити свіжі версії тулів. From 72cbca12c2c213c53ea58fed7a5d073e883478ef Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:47:40 +0200 Subject: [PATCH 25/49] docs(readme): document template workflow and upstream updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite the quickstart to lead with GitHub's "Use this template" button instead of cloning the course repo directly, so each student gets their own independent repo with a clean git history and PRs that default to their own main (not upstream). Add an 'Оновлення з курс-репо' section with the remote add + fetch + merge/cherry-pick recipe for pulling later changes from the course repo without the fork-style upstream binding. --- README.md | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ba28047..a0f2d7d 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,55 @@ # C++ для військових технологій -Навчальний репозиторій курсу. Містить dev-контейнер з C++ toolchain, стартову +Шаблон репозиторію курсу. Містить dev-контейнер з C++ toolchain, стартову структуру проєкту і окремі папки під кожну домашню роботу. +Основний спосіб користуватись цим репо - створити власну копію через кнопку +**Use this template** на GitHub. Курс-репо залишається read-only для вас, +PR-и на вашій копії йдуть у ваш main. + ## Швидкий старт -1. Склонувати репо і зайти в папку: - ```bash - git clone https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git - cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY - ``` -2. Відкрити у VS Code: +1. **Створити власну копію через Use this template:** + - Відкрити сторінку курс-репо на GitHub. + - Натиснути `Use this template` -> `Create a new repository`. + - В Owner dropdown обрати свій акаунт, задати назву (наприклад + `cpp-miltech`), створити. + + Це дає самостійне репо у вашому акаунті з чистою git-історією і без + fork-зв'язку з курс-репо. + +2. **Склонувати власний репо і відкрити у VS Code:** ```bash + git clone https://github.com//cpp-miltech.git + cd cpp-miltech code . ``` VS Code запропонує `Reopen in Container` - погодитись. Перший запуск тягне і білдить Docker image (5-10 хв), наступні запуски миттєві. -3. Всередині контейнера зібрати весь репо: +3. **Зібрати весь репо всередині контейнера:** ```bash cmake -S . -B build -G Ninja cmake --build build ``` Виконувані файли домашніх робіт з'являться в `build/homework_XX/`. +## Оновлення з курс-репо + +Якщо у курс-репо з'являється щось нове (новий starter, фікс у devcontainer, +оновлена інструкція) - підтягти у свій репо можна через додатковий remote: + +```bash +# одноразово: додати курс-репо як remote `course` +git remote add course https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git + +# коли треба оновитись: +git fetch course +git merge course/main # підтягнути все +# або точково: +git cherry-pick # конкретний коміт +``` + ## Перед стартом Потрібно: @@ -50,13 +76,16 @@ ## Як додати нову домашку +Коли лектор анонсував нову ДЗ або ви починаєте роботу над уже анонсованою: + 1. Створити папку `homework_NN/` з власним `CMakeLists.txt`, `src/`, `include/` і даними якщо є. 2. Додати у корневий CMakeLists.txt рядок: ```cmake add_subdirectory(homework_NN) ``` -3. Відкрити PR. GitHub Actions перевірить що все збирається у devcontainer-і. +3. Комітити і пушити у свій репо. CI (якщо налаштований у вашому репо) + перевірить що все збирається. ## Що робить CI From 4824e85c5bf5708677cceeaed82e8e563f25ea08 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:49:27 +0200 Subject: [PATCH 26/49] docs(changelog): add CHANGELOG.md with recent merged PRs Document PRs #3, #4, #5 and the initial block 2 content in a CHANGELOG at repo root, Keep a Changelog format. Update the README structure tree to reference the new file. --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 35 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9ee00a7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +Усі помітні зміни в цьому репо фіксуються тут. +Формат - [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), дати в ISO 8601. + +## 2026-04-25 + +### Changed + +- Devcontainer user setup спростили. Прибрали хардкод `useradd -u 1000`; + покладаємось на `updateRemoteUserUID` від VS Code і `devcontainers/ci`, який + синхронізує UID контейнерного користувача з host-ом на старті. Dockerfile + тепер лише видаляє дефолтного `ubuntu` умовно через `if id ubuntu`, щоб + звільнити слот UID 1000. + ([#5](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/5)) + +### Added + +- GitHub Actions workflow `build.yml` через `devcontainers/ci@v0.3`. CI будує + проєкт у тому самому devcontainer-і, що й локальний VS Code, запускається на + push у `main` і на кожен PR. + ([#4](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/4)) + +## 2026-04-23 + +### Added + +- Пакети `ssh` і `git` у devcontainer з коробки. Базовий git-workflow і + SSH-операції тепер працюють без донастройки. + ([#3](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/3)) +- Стартові матеріали блоку 2: корневий `CMakeLists.txt`, `homework_04/` + (starter для wheel odometry), `.devcontainer/` з Dockerfile і + initialize-скриптом, `preps/` з інструкціями для Linux, macOS, Windows 11 + + WSL2 і Dev Containers CLI. diff --git a/README.md b/README.md index a0f2d7d..3790ce0 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ git cherry-pick # конкретний коміт . ├── .devcontainer/ # Docker image + конфіг dev-контейнера ├── .github/workflows/ # GitHub Actions: CI build через devcontainer +├── CHANGELOG.md # лог помітних змін по PR-ах ├── CMakeLists.txt # корневий CMakeLists, підтягує homework_XX через add_subdirectory ├── homework_XX/ # окрема домашка, кожна зі своїм CMakeLists.txt └── preps/ # інструкції зі сетапу за платформами From 2bdaf06f11d7c68bdf65c3c723e8b77962bd563f Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:50:36 +0200 Subject: [PATCH 27/49] docs(readme): tell Windows students to clone inside WSL Quickstart step 2 runs git clone without saying where. On Windows, cloning into C:\ makes VS Code run devcontainer scripts on the Windows host, which lacks a POSIX shell and HOME/USER vars, so initializeCommand fails (error 193) and ${localEnv:...} resolves to empty strings. Add a callout before the clone command. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 3790ce0..1b17c96 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ PR-и на вашій копії йдуть у ваш main. fork-зв'язку з курс-репо. 2. **Склонувати власний репо і відкрити у VS Code:** + + > **Windows:** клонувати всередині WSL (відкрити `wsl` у PowerShell), + > не на `C:\`. Dev Containers очікує POSIX-середовище (shell-скрипти, + > `$HOME`, `$USER`); на Windows host це не працює - VS Code падає на + > `.devcontainer/scripts/initialize` (error 193), а `${localEnv:HOME}` + > і `${localEnv:USER}` порожні. Деталі - у + > [preps/windows.md](preps/windows.md). + ```bash git clone https://github.com//cpp-miltech.git cd cpp-miltech From c8ff67717e8bb000c9ff359f468eea2cffbaed4b Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:53:59 +0200 Subject: [PATCH 28/49] docs(readme): recommend Docker Engine over Docker Desktop Docker Desktop needs extra setup (WSL2 integration, file sharing, resource limits) to work with Dev Containers, and preps/ covers the Engine path only. Bump Engine to primary recommendation on all platforms and note Desktop as an optional alternative not documented here. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b17c96..af5ea25 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,10 @@ git cherry-pick # конкретний коміт ## Перед стартом Потрібно: -- **Docker** - Docker Desktop на Windows/macOS, Docker Engine на Linux. +- **Docker Engine** (рекомендовано на всіх платформах). `preps/` містить + інструкції: Linux напряму, macOS через Colima, Windows всередині WSL2. + Docker Desktop теж працює, але потребує додаткових налаштувань (WSL2 + integration, file sharing, ресурси), які `preps/` не покриває. - **VS Code** з розширенням `ms-vscode-remote.remote-containers` (Dev Containers). Покрокові інструкції під конкретну платформу: From 5366eca5ec174663a7c56759b03cb6855d0a98ee Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 00:57:41 +0200 Subject: [PATCH 29/49] docs(readme): note how to give lecturer access for review Without adding yevhenkuznetsov as a collaborator, the lecturer cannot see private student repos at all and cannot submit formal reviews (approve/request changes) even on public ones. --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index af5ea25..9de19c3 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,17 @@ PR-и на вашій копії йдуть у ваш main. ``` Виконувані файли домашніх робіт з'являться в `build/homework_XX/`. +## Доступ для перевірки + +Додати лектора як collaborator свого репо, щоб він міг бачити код і робити +review на pull request-ах: + +- `Settings` -> `Collaborators` -> `Add people` -> `yevhenkuznetsov`, роль + `Read` достатня. + +Без цього лектор не побачить приватний репо взагалі і не зможе залишати +formal review (approve / request changes) навіть на публічному. + ## Оновлення з курс-репо Якщо у курс-репо з'являється щось нове (новий starter, фікс у devcontainer, From 7249afd45328cc915d6c775ce1f82be0b61a28e3 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sat, 25 Apr 2026 01:12:26 +0200 Subject: [PATCH 30/49] docs(readme): add farnblack as second lecturer contact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The course has two lecturers; the 'Доступ для перевірки' section only listed yevhenkuznetsov. Add farnblack alongside so students grant collaborator access to both when creating their repo. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9de19c3..acbab52 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,15 @@ PR-и на вашій копії йдуть у ваш main. ## Доступ для перевірки -Додати лектора як collaborator свого репо, щоб він міг бачити код і робити -review на pull request-ах: +Додати обох лекторів як collaborators свого репо, щоб вони могли бачити +код і робити review на pull request-ах: - `Settings` -> `Collaborators` -> `Add people` -> `yevhenkuznetsov`, роль `Read` достатня. +- `Settings` -> `Collaborators` -> `Add people` -> `farnblack`, роль + `Read` достатня. -Без цього лектор не побачить приватний репо взагалі і не зможе залишати +Без цього лектори не побачать приватний репо взагалі і не зможуть залишати formal review (approve / request changes) навіть на публічному. ## Оновлення з курс-репо From 02318237cecd5f170b47db0aa252ac2252b4a100 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Sun, 26 Apr 2026 21:20:24 +0200 Subject: [PATCH 31/49] docs(readme): drop git-sync recipes, route updates through Slack Cherry-pick / merge / remote add for syncing course-repo changes turn out brittle: template/manual-copy student repos have no shared history with course/main, so merge fails outright and cherry-pick can conflict on locally-modified files. Replace the section with a one-paragraph note that any meaningful course-repo change comes with a Slack announcement and explicit instructions on what to replace by hand. Keep the explanation of why git-sync doesn't work, since that question will keep coming up. --- README.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index acbab52..806dfa2 100644 --- a/README.md +++ b/README.md @@ -57,19 +57,26 @@ formal review (approve / request changes) навіть на публічному ## Оновлення з курс-репо -Якщо у курс-репо з'являється щось нове (новий starter, фікс у devcontainer, -оновлена інструкція) - підтягти у свій репо можна через додатковий remote: - -```bash -# одноразово: додати курс-репо як remote `course` -git remote add course https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git - -# коли треба оновитись: -git fetch course -git merge course/main # підтягнути все -# або точково: -git cherry-pick # конкретний коміт -``` +Зміни в курс-репо не синкаються автоматично у локальний репо. Якщо +щось важливе оновлюється - буде анонс у Slack-каналі курсу з +інструкцією, що замінити вручну. + +**Чому не git merge / cherry-pick:** курс-репо позначений як GitHub +Template. Копія через "Use this template" - це новий репо з єдиним +"Initial commit", без зв'язку з курс-репо в історії. Спільного предка +нема, тому git нічого не може злити автоматично. + +Чому Template, а не fork: fork залишає історію спільною, і merge для +нього працював би. Але PR на fork-у GitHub дефолтно цілить у upstream +(у курс-репо). Курс-репо має лишатися read-only, PR-и студентів - іти +у їхні репо. Template це знімає, ціна - нема прямого git-sync. + +`git merge course/main` падає з `refusing to merge unrelated histories`. +З `--allow-unrelated-histories` merge пройде, але вийде каша: дві +кореневі коміти і конфлікти на більшості файлів. Cherry-pick не падає +сам, але конфліктує на локально модифікованих файлах. + +Простіше - ручна заміна файлів за Slack-анонсом. Без git-акробатики. ## Перед стартом From ac61115add1df3078f17b9b83546d3514d6fe8e8 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 12:10:27 +0200 Subject: [PATCH 32/49] docs(preps): align setup docs with template flow Block 2 / Lessons 2.1-2.2 Update setup instructions to use the GitHub template workflow instead of the old lesson branch. Adjust the verification step to build the current homework target. --- preps/README.md | 26 +++++++++++++++++--------- preps/linux.md | 9 ++++++--- preps/macos.md | 9 ++++++--- preps/windows.md | 10 ++++++---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/preps/README.md b/preps/README.md index 573c3c6..b6da9b0 100644 --- a/preps/README.md +++ b/preps/README.md @@ -5,17 +5,23 @@ ## Кроки 1. **Поставити Docker** - див. [linux.md](linux.md) / [windows.md](windows.md) / [macos.md](macos.md) для своєї ОС. -2. **Склонувати репо курсу** (гілка `block-2-lesson-1`). Команди в тому ж OS-файлі, секція "Як взяти код курсу". На Windows - клон йде **в WSL**, не на `C:\`. -3. **Відкрити у VS Code**: `code .` з Ubuntu-терміналу після клону (не з Windows Explorer - `devcontainer.json` цього проекту зламається без WSL-сесії). -4. **Встановити Dev Containers extension**: VS Code запропонує сам (із `.vscode/extensions.json`). Якщо ні - `Ctrl+Shift+X` (на macOS `Cmd+Shift+X`), пошук `Dev Containers`, Install. -5. **Reopen in Container**: VS Code запропонує - погодитись. Перший білд 3-5 хв. +2. **Створити власний репо з template** через `Use this template` на GitHub. +3. **Склонувати власний репо**. Команди в тому ж OS-файлі, секція "Як взяти код". На Windows - клон йде **в WSL**, не на `C:\`. +4. **Відкрити у VS Code**: `code .` з Ubuntu-терміналу після клону (не з Windows Explorer - `devcontainer.json` цього проекту зламається без WSL-сесії). +5. **Встановити Dev Containers extension**: VS Code запропонує сам (із `.vscode/extensions.json`). Якщо ні - `Ctrl+Shift+X` (на macOS `Cmd+Shift+X`), пошук `Dev Containers`, Install. +6. **Reopen in Container**: VS Code запропонує - погодитись. Перший білд 3-5 хв. ## Якщо вже клонували репо раніше -Якщо репо клоновано до нових оновлень (фікси для devcontainer) - підтягнути зміни. В Ubuntu/shell-терміналі: +Якщо репо клоновано до нових оновлень - дочекатися Slack-анонсу від лектора +і замінити вказані файли вручну. Курс-репо створене як GitHub Template, тому +звичайний `git pull` з upstream не працює надійно для студентських копій. + +Якщо клонована саме власна копія репозиторію, локальні зміни з неї +підтягуються як звичайно: ```bash -cd ~/projects/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +cd ~/projects/cpp-miltech git pull ``` @@ -32,9 +38,11 @@ git pull Всередині контейнера (VS Code terminal): ```bash -cmake -S . -B build +cmake -S . -B build -G Ninja cmake --build build -./build/app +./build/homework_04/ugv_odometry homework_04/data/straight.txt ``` -Має вивести `Hello, section 2!`. Якщо не працює - в чат курсу. +Перші дві команди мають завершитись без помилки. Остання команда запускає +стартовий executable для ДЗ 4; у стартовій версії він може нічого не друкувати, +бо реалізація ще є частиною домашнього завдання. Якщо збірка не працює - в чат курсу. diff --git a/preps/linux.md b/preps/linux.md index 6ee61f6..f95ea89 100644 --- a/preps/linux.md +++ b/preps/linux.md @@ -22,7 +22,10 @@ sudo systemctl enable docker docker run hello-world ``` -## Як взяти код курсу +## Як взяти код + +Спочатку створити власний репозиторій через `Use this template` на сторінці +курс-репо в GitHub. Потім клонувати власний репозиторій: ```bash sudo apt update && sudo apt install -y git # або еквівалент вашого пакетного менеджера @@ -32,8 +35,8 @@ git config --global user.email "you@example.com" mkdir -p ~/projects cd ~/projects -git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git -cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +git clone https://github.com//cpp-miltech.git +cd cpp-miltech code . ``` diff --git a/preps/macos.md b/preps/macos.md index e6a11ff..f0d3808 100644 --- a/preps/macos.md +++ b/preps/macos.md @@ -36,7 +36,10 @@ brew services start colima На Apple Silicon (M1/M2/M3/M4) все запускається нативно як `arm64`. Якщо раптом попаде образ тільки `amd64` - `docker run --platform linux/amd64 ...` (повільніше, через qemu). -## Як взяти код курсу +## Як взяти код + +Спочатку створити власний репозиторій через `Use this template` на сторінці +курс-репо в GitHub. Потім клонувати власний репозиторій: ```bash git config --global user.name "Your Name" @@ -44,8 +47,8 @@ git config --global user.email "you@example.com" mkdir -p ~/projects cd ~/projects -git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git -cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +git clone https://github.com//cpp-miltech.git +cd cpp-miltech code . ``` diff --git a/preps/windows.md b/preps/windows.md index b1dcf63..210b7c2 100644 --- a/preps/windows.md +++ b/preps/windows.md @@ -46,9 +46,11 @@ docker run hello-world **Важливо:** код тримати в `~/projects/...` всередині WSL, не на `C:\`. Через `/mnt/c/` IO в рази повільніше, VS Code буде нестерпно гальмувати. -## Як взяти код курсу +## Як взяти код -Клон йде в WSL (не на `C:\`). В Ubuntu-терміналі: +Спочатку створити власний репозиторій через `Use this template` на сторінці +курс-репо в GitHub. Потім клонувати власний репозиторій у WSL (не на `C:\`). +В Ubuntu-терміналі: ```bash sudo apt update && sudo apt install -y git @@ -58,8 +60,8 @@ git config --global user.email "you@example.com" mkdir -p ~/projects cd ~/projects -git clone --branch block-2-lesson-1 https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git -cd C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY +git clone https://github.com//cpp-miltech.git +cd cpp-miltech code . ``` From 58851e762a0608d728d7a976d63ef8af28c968c2 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 12:10:33 +0200 Subject: [PATCH 33/49] chore(clangd): wire clangd to CMake compile database Block 2 / Lesson 2.3 Export compile_commands.json from the root CMake project and point clangd at the generated build directory inside the devcontainer. --- .devcontainer/devcontainer.json | 2 +- CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ad5594b..488879b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,7 +33,7 @@ "settings": { "clangd.path": "/usr/bin/clangd", "clangd.arguments": [ - "--compile-commands-dir=ada_ws/build/x86_64/", + "--compile-commands-dir=build", "--clang-tidy", "--background-index" ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 4025f7a..e6e3b1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(section2 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) From 760f005c0802c2572cdd12935fe02505abd50728 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 12:10:37 +0200 Subject: [PATCH 34/49] chore(clang-tools): add clang-tidy course config Block 2 / Lesson 2.6 Add the course clang-tidy configuration, install clang-tidy in the devcontainer, and copy the config into the container root alongside the existing clang-format setup. --- .devcontainer/.clang-tidy | 43 +++++++++++++++++++++++++++++++++++ .devcontainer/Dockerfile | 1 + .devcontainer/apt-packages.in | 1 + 3 files changed, 45 insertions(+) create mode 100644 .devcontainer/.clang-tidy diff --git a/.devcontainer/.clang-tidy b/.devcontainer/.clang-tidy new file mode 100644 index 0000000..461712d --- /dev/null +++ b/.devcontainer/.clang-tidy @@ -0,0 +1,43 @@ +Checks: > + -*, + bugprone-*, + clang-analyzer-*, + cppcoreguidelines-*, + modernize-*, + performance-*, + readability-*, + -readability-avoid-const-params-in-decls, + -readability-function-cognitive-complexity, + misc-unused-using-decls, + +HeaderFilterRegex: '(^|/)(homework_[0-9]+|include|src)/' + +WarningsAsErrors: clang-analyzer-*, + cppcoreguidelines-*, + modernize-*, + +CheckOptions: + - key: readability-identifier-naming.FunctionCase + value: lower_case + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ConstantCase + value: CamelCase + - key: readability-identifier-naming.ConstantPrefix + value: k + - key: readability-identifier-naming.LocalVariableCase + value: lower_case + - key: readability-identifier-naming.ParameterCase + value: lower_case + - key: readability-identifier-naming.MemberCase + value: lower_case + - key: readability-identifier-naming.MemberSuffix + value: _ + - key: readability-identifier-naming.PrivateMemberCase + value: lower_case + - key: readability-identifier-naming.PrivateMemberSuffix + value: _ + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.StructCase + value: CamelCase diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4c3ba82..33cc01b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -39,6 +39,7 @@ RUN apt update && \ # запуску - тобто вони знайдуться незалежно від того, де всередині # контейнера змонтовано проєкт. COPY .clang-format /.clang-format +COPY .clang-tidy /.clang-tidy COPY .clangd /.clangd COPY .cmake-format.json /.cmake-format.json diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index 91a0a3a..69453d0 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -9,6 +9,7 @@ git gcc clang clangd +clang-tidy make cmake clang-format From 274ca56da49fdb86b6b58566d659c63e5a04d936 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 12:10:42 +0200 Subject: [PATCH 35/49] chore(devcontainer): add debug tools for diagnostics Block 2 / Lesson 2.4 Install the GDB and Valgrind tools needed for diagnostics demos and homework. Also keep the devcontainer terminal settings inside VS Code settings and remove the trailing mount entry comma so the file is valid JSON. --- .devcontainer/apt-packages.in | 4 ++++ .devcontainer/devcontainer.json | 18 +++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index 69453d0..2a6f188 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -10,6 +10,10 @@ gcc clang clangd clang-tidy +gdb +gdbserver +gdb-multiarch +valgrind make cmake clang-format diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 488879b..e3fd050 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,7 @@ "workspaceFolder": "${localEnv:HOME}/${localWorkspaceFolderBasename}", "mounts": [ "source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,readonly,consistency=cached", - "source=${localEnv:HOME}/.devcontainers/${localWorkspaceFolderBasename}/.bash_history,target=/home/${localEnv:USER}/.bash_history,type=bind,consistency=delegated", + "source=${localEnv:HOME}/.devcontainers/${localWorkspaceFolderBasename}/.bash_history,target=/home/${localEnv:USER}/.bash_history,type=bind,consistency=delegated" ], "initializeCommand": [ ".devcontainer/scripts/initialize" @@ -53,14 +53,14 @@ "cmakeFormat.args": [ "--config-file=/.cmake-format.json" ], - "cmakeFormat.exePath": "/usr/bin/cmake-format" - } - }, - "terminal.integrated.shell.linux": "bash", - "terminal.integrated.profiles.linux": { - "bash (container default)": { - "path": "/bin/bash", - "overrideName": true + "cmakeFormat.exePath": "/usr/bin/cmake-format", + "terminal.integrated.defaultProfile.linux": "bash (container default)", + "terminal.integrated.profiles.linux": { + "bash (container default)": { + "path": "/bin/bash", + "overrideName": true + } + } } } } From ce36e67be86a0097e52de04c2764fad23cf0dd24 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 12:48:27 +0200 Subject: [PATCH 36/49] docs(config): document student-facing setup files Block 2 / Lessons 2.3-2.6 Add Ukrainian comments to CMake, devcontainer, formatter/linter configs, Git settings, VS Code host recommendation, and homework starter code so student-facing files explain why each block exists. --- .devcontainer/.clang-format | 5 +- .devcontainer/.clang-tidy | 9 ++++ .devcontainer/.clangd | 6 +++ .devcontainer/.cmake-format.json | 5 ++ .devcontainer/Dockerfile | 6 +-- .devcontainer/apt-packages.in | 6 +++ .devcontainer/devcontainer.json | 28 +++++++++++ .devcontainer/scripts/initialize | 10 ++-- .gitattributes | 5 ++ .gitignore | 84 +++++++------------------------- .vscode/extensions.json | 3 ++ CMakeLists.txt | 8 +++ homework_04/CMakeLists.txt | 7 +++ homework_04/src/main.cpp | 9 ++-- 14 files changed, 112 insertions(+), 79 deletions(-) diff --git a/.devcontainer/.clang-format b/.devcontainer/.clang-format index d4c6dda..9a61cf3 100644 --- a/.devcontainer/.clang-format +++ b/.devcontainer/.clang-format @@ -1,6 +1,9 @@ --- Language: Cpp -# BasedOnStyle: Google +# Єдиний стиль форматування C++ прикладів у курсі. +# Файл копіюється в корінь devcontainer-а, щоб clang-format знаходив його +# незалежно від директорії, з якої запускається команда. +# BasedOnStyle: Google AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false diff --git a/.devcontainer/.clang-tidy b/.devcontainer/.clang-tidy index 461712d..0aa217a 100644 --- a/.devcontainer/.clang-tidy +++ b/.devcontainer/.clang-tidy @@ -1,3 +1,6 @@ +# clang-tidy використовується як static analysis шар для C++ коду. +# Набір перевірок навмисно широкий, але не всі warning-и є помилками: +# частину сигналу студенти мають навчитися читати поступово. Checks: > -*, bugprone-*, @@ -10,12 +13,18 @@ Checks: > -readability-function-cognitive-complexity, misc-unused-using-decls, +# Аналізувати лише код домашніх робіт і типові source/include директорії, +# а не системні headers або сторонні бібліотеки. HeaderFilterRegex: '(^|/)(homework_[0-9]+|include|src)/' +# Помилки analyzer/core-guidelines/modernize мають ламати перевірку. +# Readability і performance лишаються попередженнями, щоб не блокувати +# навчальні ітерації надто рано. WarningsAsErrors: clang-analyzer-*, cppcoreguidelines-*, modernize-*, +# Naming rules формують один стиль для прикладів курсу. CheckOptions: - key: readability-identifier-naming.FunctionCase value: lower_case diff --git a/.devcontainer/.clangd b/.devcontainer/.clangd index 8a3e3af..e12fb3a 100644 --- a/.devcontainer/.clangd +++ b/.devcontainer/.clangd @@ -1,4 +1,10 @@ +# clangd працює як language server у VS Code. +# Основна інформація про include paths і compile flags береться з +# compile_commands.json, який генерує root CMakeLists.txt. InlayHints: Enabled: Yes + # Імена параметрів у викликах не показуються, щоб не перевантажувати + # екран на простих прикладах. ParameterNames: No + # Типи, виведені через auto, корисно бачити під час навчання C++. DeducedTypes: Yes diff --git a/.devcontainer/.cmake-format.json b/.devcontainer/.cmake-format.json index 6a97078..023c071 100644 --- a/.devcontainer/.cmake-format.json +++ b/.devcontainer/.cmake-format.json @@ -1,4 +1,9 @@ { + "_course_note": [ + "Конфіг cmake-format для CMakeLists.txt у курсі.", + "Файл копіюється в корінь devcontainer-а, щоб formatter знаходив", + "його незалежно від поточної директорії запуску." + ], "_help_parse": "Options affecting listfile parsing", "parse": { "_help_additional_commands": [ diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 33cc01b..57b4765 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -12,7 +12,7 @@ # docker inspect --format='{{index .RepoDigests 0}}' ubuntu:noble FROM ubuntu:noble@sha256:c4a8d5503dfb2a3eb8ab5f807da5bc69a85730fb49b5cfca2330194ebcc41c7b -# Noninteractive frontend, щоб apt не зависнув чекаючи відповіді на +# Неінтерактивний frontend, щоб apt не зависнув чекаючи відповіді на # питання (часовий пояс, клавіатура, kernel upgrade prompt). У Dockerfile # інтерактивного stdin немає - без цього build може зависнути назавжди. ARG DEBIAN_FRONTEND="noninteractive" @@ -43,7 +43,7 @@ COPY .clang-tidy /.clang-tidy COPY .clangd /.clangd COPY .cmake-format.json /.cmake-format.json -# Environment: +# Змінні середовища: # - TZ: UTC як нейтральний дефолт. Логи завжди в UTC, без суперечок з # літнім часом різних регіонів. # - LC_ALL / LANG / LANGUAGE: en_US.UTF-8 щоб stdout/stderr коректно @@ -53,7 +53,7 @@ ENV LC_ALL=en_US.UTF-8 ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en -# Passwordless sudo для dev-контейнера: зручно для apt install на ходу, +# sudo без пароля для dev-контейнера: зручно для apt install на ходу, # debug tools, etc. У прод-образах ніколи - там контейнер має жорстко # визначений набір прав. RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index 2a6f188..e6e4367 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -1,3 +1,4 @@ +# Базові пакети для стабільної роботи Ubuntu image. ca-certificates curl locales @@ -6,14 +7,19 @@ tzdata ssh git +# C++ toolchain і language server для редактора. gcc clang clangd clang-tidy + +# Інструменти для заняття 2.4: локальний і remote debug, memory diagnostics. gdb gdbserver gdb-multiarch valgrind + +# Інструменти збірки і форматери, які використовуються в лекціях та CI. make cmake clang-format diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e3fd050..79a80a8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,9 @@ { + // Назва, яка показується у VS Code Dev Containers UI. "name": "CPP MilTech", + + // Image збирається з локального Dockerfile. Аргументи потрібні, щоб + // container user і workspace path збігалися з host-сесією. "build": { "dockerfile": "Dockerfile", "args": { @@ -7,23 +11,41 @@ "CONTAINER_USER": "${localEnv:USER}" } }, + + // VS Code відкриває контейнер від імені того самого користувача, що й host. + // Це зменшує проблеми з ownership файлів у bind mount. "remoteUser": "${localEnv:USER}", + + // Проєкт монтується у домашню директорію користувача всередині контейнера. + // Так шляхи виглядають однаково на Linux, macOS і Windows+WSL. "workspaceMount": "source=${localWorkspaceFolder},target=${localEnv:HOME}/${localWorkspaceFolderBasename},type=bind", "workspaceFolder": "${localEnv:HOME}/${localWorkspaceFolderBasename}", + + // SSH ключі потрібні для git operations. bash_history винесено на host, + // щоб історія команд переживала rebuild контейнера. "mounts": [ "source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,readonly,consistency=cached", "source=${localEnv:HOME}/.devcontainers/${localWorkspaceFolderBasename}/.bash_history,target=/home/${localEnv:USER}/.bash_history,type=bind,consistency=delegated" ], + + // initialize створює host-side директорії для mounts до старту container-а. "initializeCommand": [ ".devcontainer/scripts/initialize" ], + + // host network спрощує SSH/debug сценарії для занять з target-пристроями. "runArgs": [ "--network=host", "--hostname=devcontainer", "--add-host=devcontainer:127.0.1.1" ], + + // Усе всередині customizations.vscode застосовується саме до VS Code + // сесії в контейнері. Це не те саме, що .vscode/extensions.json на host. "customizations": { "vscode": { + // Extensions встановлюються в devcontainer, щоб tooling був однаковий + // у всіх студентів незалежно від host VS Code. "extensions": [ "josetr.cmake-language-support-vscode", "cheshirekow.cmake-format", @@ -31,6 +53,7 @@ "llvm-vs-code-extensions.vscode-clangd" ], "settings": { + // clangd читає build/compile_commands.json, який генерується CMake. "clangd.path": "/usr/bin/clangd", "clangd.arguments": [ "--compile-commands-dir=build", @@ -38,6 +61,8 @@ "--background-index" ], "clangd.restartAfterCrash": true, + // IntelliSense від Microsoft C++ extension вимкнено, щоб не було двох + // C++ language server-ів одночасно. Основний engine - clangd. "C_Cpp.intelliSenseEngine": "disabled", "[cpp]": { "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", @@ -50,10 +75,13 @@ "[cmake]": { "editor.defaultFormatter": "cheshirekow.cmake-format" }, + // cmake-format читає конфіг, який Dockerfile копіює в /. "cmakeFormat.args": [ "--config-file=/.cmake-format.json" ], "cmakeFormat.exePath": "/usr/bin/cmake-format", + // Єдиний bash profile у container terminal. Історія команд монтується + // окремо через mounts вище. "terminal.integrated.defaultProfile.linux": "bash (container default)", "terminal.integrated.profiles.linux": { "bash (container default)": { diff --git a/.devcontainer/scripts/initialize b/.devcontainer/scripts/initialize index 20a1d63..e484665 100755 --- a/.devcontainer/scripts/initialize +++ b/.devcontainer/scripts/initialize @@ -1,13 +1,13 @@ #!/usr/bin/env bash set -euo pipefail -# Runs on the host (macOS, Linux, WSL2) before the container starts. -# Creates a bash history file under the host's home so shell history -# persists across container rebuilds. +# Скрипт виконується на host-системі (macOS, Linux, WSL2) до старту +# контейнера. Тут створюються host-side файли, які потім монтуються +# у devcontainer. DEVCONTAINER_DATA_ON_HOST="${HOME}/.devcontainers/$(basename "${PWD}")" mkdir -p "${DEVCONTAINER_DATA_ON_HOST}" touch "${DEVCONTAINER_DATA_ON_HOST}/.bash_history" -# Ensure ~/.ssh exists so the bind-mount in devcontainer.json does not fail -# on fresh machines where the user never generated SSH keys yet. +# ~/.ssh має існувати до bind mount з devcontainer.json. На свіжих машинах +# SSH ключів може ще не бути, і без цієї директорії container start падає. [ -d "${HOME}/.ssh" ] || mkdir -m 700 "${HOME}/.ssh" diff --git a/.gitattributes b/.gitattributes index 795b063..7029cd4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,14 @@ +# Нормалізувати текстові файли до LF, щоб приклади однаково працювали на +# Linux, macOS і Windows+WSL. * text=auto eol=lf +# Shell scripts і devcontainer hooks завжди мають LF, інакше bash у Linux +# контейнері може падати на CRLF. *.sh text eol=lf .devcontainer/scripts/* text eol=lf Dockerfile text eol=lf +# Бінарні assets не проходять text normalization. *.png binary *.jpg binary *.jpeg binary diff --git a/.gitignore b/.gitignore index b27363a..8d16481 100644 --- a/.gitignore +++ b/.gitignore @@ -1,79 +1,31 @@ -# Prerequisites -*.d +# Директорії збірки та локальний output мають відтворюватися з CMake. +build/ +build-*/ +out/ -# Compiled Object files -*.slo -*.lo +# CMake генерує ці файли при in-source або ручних локальних збірках. +CMakeCache.txt +CMakeFiles/ +CTestTestfile.cmake +Makefile +cmake_install.cmake +compile_commands.json +install_manifest.txt +_deps/ + +# Мінімальний набір compiled/debug артефактів для C++ прикладів. +*.d *.o *.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries +*.a *.so *.dylib *.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables *.exe *.out -*.app - -# Debug files *.dSYM/ -*.su -*.idb *.pdb -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -# CMake -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps - -# Build directories -build/ -build-*/ -out/ -bin/ -obj/ - -# IDE / editor -.vs/ -.idea/ -*.swp -*.swo -*~ -.cache/ - -# OS +# Метадані операційної системи не є частиною навчального репозиторію. .DS_Store Thumbs.db diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e6f2364..bd817ad 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,4 +1,7 @@ { + // Рекомендація тільки для host VS Code: цей extension потрібен на машині + // студента, щоб відкрити репозиторій у devcontainer. C++ інструменти + // встановлюється всередині контейнера через .devcontainer/devcontainer.json. "recommendations": [ "ms-vscode-remote.remote-containers" ] diff --git a/CMakeLists.txt b/CMakeLists.txt index e6e3b1f..8f152e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,17 @@ cmake_minimum_required(VERSION 3.20) + +# Загальний CMake-проєкт для домашніх робіт блоку 2. +# Кожна homework_* директорія підключається окремо через add_subdirectory(). project(section2 LANGUAGES CXX) +# C++20 - базовий стандарт курсу. EXTENSIONS OFF прибирає GNU-розширення, +# щоб код лишався ближчим до переносимого ISO C++. set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) + +# clangd читає compile_commands.json і завдяки цьому бачить ті самі include +# paths, defines і flags, що й реальна CMake-збірка. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. diff --git a/homework_04/CMakeLists.txt b/homework_04/CMakeLists.txt index b7c94c2..bc6a70a 100644 --- a/homework_04/CMakeLists.txt +++ b/homework_04/CMakeLists.txt @@ -1,9 +1,16 @@ cmake_minimum_required(VERSION 3.20) + +# Окремий CMake-проєкт для домашньої роботи 04. Його можна збирати як +# через root CMakeLists.txt, так і напряму з цієї директорії. project(ugv_odometry CXX) +# Код домашньої роботи збирається з тим самим стандартом, що й лекційні приклади. set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +# Один executable для задачі з UGV odometry. add_executable(ugv_odometry src/main.cpp) + +# Базові compiler warnings допомагають побачити підозрілі місця до runtime. target_compile_options(ugv_odometry PRIVATE -Wall -Wextra -pedantic) diff --git a/homework_04/src/main.cpp b/homework_04/src/main.cpp index 42894b6..4fb7a8c 100644 --- a/homework_04/src/main.cpp +++ b/homework_04/src/main.cpp @@ -1,21 +1,22 @@ #include int main(int argc, char** argv) { + // Програма очікує рівно один аргумент: шлях до файлу з telemetry samples. if (argc != 2) { std::cerr << "usage: ugv_odometry \n"; return 1; } - // TODO: implement wheel odometry for a 4-wheel differential-drive UGV. + // TODO: реалізувати wheel odometry для 4-колісної differential-drive UGV. // - // Parameters: + // Параметри моделі: // ticks_per_revolution = 1024 // wheel_radius_m = 0.3 // wheelbase_m = 1.0 // - // Input: text file with 5 whitespace-separated numbers per line: + // Вхід: текстовий файл з 5 числами в кожному рядку: // timestamp_ms fl_ticks fr_ticks bl_ticks br_ticks - // Output: same tabular format on stdout, starting from the second sample: + // Вихід: табличний формат у stdout, починаючи з другого sample: // timestamp_ms x y theta return 0; From 54eaef8533b6ef94fd10beb6131de82b9597c605 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 13:11:45 +0200 Subject: [PATCH 37/49] docs(homework): keep C++ comments in English Block 2 / Lesson 2.4 Translate homework starter C++ comments to English while keeping student-facing configuration comments in Ukrainian. --- homework_04/src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homework_04/src/main.cpp b/homework_04/src/main.cpp index 4fb7a8c..9130028 100644 --- a/homework_04/src/main.cpp +++ b/homework_04/src/main.cpp @@ -1,22 +1,22 @@ #include int main(int argc, char** argv) { - // Програма очікує рівно один аргумент: шлях до файлу з telemetry samples. + // The program expects exactly one argument: a path to telemetry samples. if (argc != 2) { std::cerr << "usage: ugv_odometry \n"; return 1; } - // TODO: реалізувати wheel odometry для 4-колісної differential-drive UGV. + // TODO: implement wheel odometry for a 4-wheel differential-drive UGV. // - // Параметри моделі: + // Model parameters: // ticks_per_revolution = 1024 // wheel_radius_m = 0.3 // wheelbase_m = 1.0 // - // Вхід: текстовий файл з 5 числами в кожному рядку: + // Input: a text file with 5 whitespace-separated values per line: // timestamp_ms fl_ticks fr_ticks bl_ticks br_ticks - // Вихід: табличний формат у stdout, починаючи з другого sample: + // Output: a table on stdout, starting from the second sample: // timestamp_ms x y theta return 0; From 8dbff8a5017a89fcbd8610996a41195dcca8fd53 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 14:25:04 +0200 Subject: [PATCH 38/49] chore(vscode): add build and clean tasks Block 2 / Lessons 2.3-2.4 Add VS Code tasks for configuring/building the CMake project and cleaning the local build directory. --- .vscode/tasks.json | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..aabd11f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,37 @@ +{ + // VS Code tasks виконуються з кореня workspace у devcontainer terminal. + // Тут лишаються тільки базові команди: configure/build і clean. + "version": "2.0.0", + "tasks": [ + { + // Default build task: Ctrl+Shift+B запускає CMake configure, потім build. + "label": "CMake: configure and build", + "type": "shell", + "command": "cmake -S . -B build -G Ninja && cmake --build build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + }, + { + // Clean task навмисно простий: видаляється тільки локальна build директорія. + "label": "CMake: clean build directory", + "type": "shell", + "command": "rm -rf build", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + } + ] +} From 43a14f28dc269dcfe946f076c97e3435a63bdb3b Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 14:32:53 +0200 Subject: [PATCH 39/49] docs(changelog): record tooling updates Block 2 / Lessons 2.1-2.6 Document the clangd, clang-tidy, debug tooling, VS Code tasks, setup docs, and student-facing config updates from the latest repository changes. --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ee00a7..b6e07dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ Усі помітні зміни в цьому репо фіксуються тут. Формат - [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), дати в ISO 8601. +## 2026-04-30 + +### Added + +- Block 2 / Lessons 2.3-2.4: VS Code tasks для CMake configure/build і + видалення локальної `build/` директорії. `CMake: configure and build` + зроблено default build task для `Ctrl+Shift+B`. + ([#10](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/10)) +- Block 2 / Lessons 2.3-2.6: clang-tidy конфіг, debug/diagnostics tools + (`gdb`, `gdbserver`, `gdb-multiarch`, `valgrind`) і налаштування clangd через + `build/compile_commands.json`. + ([#9](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/9)) + +### Changed + +- Block 2 / Lessons 2.1-2.6: оновлено setup docs під GitHub Template flow, + додано пояснювальні коментарі до student-facing tooling/config файлів і + зменшено `.gitignore` до мінімального C++/CMake набору. + ([#9](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/9)) + ## 2026-04-25 ### Changed From ff56262ca6baeae570daea1584761cc31b2dd4fb Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 16:29:07 +0200 Subject: [PATCH 40/49] feat(block-2-lesson-2-4): add local and remote GDB demo Block: 2 Lesson: 2.4 --- CMakeLists.txt | 7 +- demos/lesson_2_4/remote_debug/CMakeLists.txt | 20 ++ demos/lesson_2_4/remote_debug/README.md | 178 ++++++++++++++++++ .../remote_debug/data/bad_missing_field.txt | 1 + demos/lesson_2_4/remote_debug/data/good.txt | 1 + demos/lesson_2_4/remote_debug/src/main.cpp | 120 ++++++++++++ 6 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 demos/lesson_2_4/remote_debug/CMakeLists.txt create mode 100644 demos/lesson_2_4/remote_debug/README.md create mode 100644 demos/lesson_2_4/remote_debug/data/bad_missing_field.txt create mode 100644 demos/lesson_2_4/remote_debug/data/good.txt create mode 100644 demos/lesson_2_4/remote_debug/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f152e3..bc656f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) -# Загальний CMake-проєкт для домашніх робіт блоку 2. -# Кожна homework_* директорія підключається окремо через add_subdirectory(). +# Загальний CMake-проєкт для матеріалів блоку 2. +# Кожна homework_* або demos/* директорія підключається окремо. project(section2 LANGUAGES CXX) # C++20 - базовий стандарт курсу. EXTENSIONS OFF прибирає GNU-розширення, @@ -16,3 +16,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) + +# Demo-код для заняття 2.4: локальний і remote debug через GDB. +add_subdirectory(demos/lesson_2_4/remote_debug) diff --git a/demos/lesson_2_4/remote_debug/CMakeLists.txt b/demos/lesson_2_4/remote_debug/CMakeLists.txt new file mode 100644 index 0000000..64ea084 --- /dev/null +++ b/demos/lesson_2_4/remote_debug/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.20) + +# Цей CMakeLists можна збирати двома способами: +# - як частину кореневого репозиторію через add_subdirectory(); +# - окремо на target-пристрої, якщо скопійовано лише цю demo-директорію. +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(remote_debug_demo LANGUAGES CXX) + + # Standalone-збірка використовує той самий C++20, що й основний курс. + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif() + +add_executable(debug_probe src/main.cpp) + +# Warning flags тримаємо на target-рівні, щоб не впливати на інші приклади. +target_compile_options( + debug_probe + PRIVATE $<$:-Wall -Wextra -Wpedantic>) diff --git a/demos/lesson_2_4/remote_debug/README.md b/demos/lesson_2_4/remote_debug/README.md new file mode 100644 index 0000000..a5b5f74 --- /dev/null +++ b/demos/lesson_2_4/remote_debug/README.md @@ -0,0 +1,178 @@ +# Демо 2.4: локальний і віддалений GDB + +Цей приклад використовується на занятті 2.4 для показу одного циклу +відлагодження: + +```text +відтворити -> оглянути -> підтвердити -> виправити -> перевірити +``` + +Код дуже малий і має дві навмисні помилки під час виконання. Одна проявляється +як падіння на некоректних вхідних даних, друга добре видна через Valgrind на +коректних вхідних даних. Точні місця в коді не позначено, щоб демо проходило +через інструменти, а не через читання готової підказки. + +## Локальний запуск + +```bash +cmake --preset debug +cmake --build --preset debug + +./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ + demos/lesson_2_4/remote_debug/data/good.txt +``` + +Очікуваний вивід: + +```text +seq 7 +battery_v 24.6 +satellites 12 +health ready +``` + +## Консольний GDB + +```bash +gdb ./build/debug/demos/lesson_2_4/remote_debug/debug_probe +``` + +Всередині GDB: + +```text +run demos/lesson_2_4/remote_debug/data/bad_missing_field.txt +bt +frame 0 +print text +up +print field_count +print fields[2] +quit +``` + +## Valgrind + +```bash +valgrind --leak-check=full \ + ./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ + demos/lesson_2_4/remote_debug/data/good.txt +``` + +Очікуваний сигнал: `definitely lost` memory block. + +Проблемні вхідні дані також можна запускати через Valgrind, щоб побачити +некоректне читання: + +```bash +valgrind \ + ./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ + demos/lesson_2_4/remote_debug/data/bad_missing_field.txt +``` + +## Cross build для Raspberry Pi + +```bash +cmake --preset aarch64-debug +cmake --build --preset aarch64-debug +``` + +ARM64 виконуваний файл: + +```text +build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe +``` + +## Remote run на Raspberry Pi + +На `satelite` потрібен `gdbserver`, але не компілятор: + +```bash +ssh satelite +sudo apt update +sudo apt install -y gdbserver +exit +``` + +Скопіювати виконуваний файл і дані: + +```bash +ssh satelite 'rm -rf /tmp/remote_debug_demo' +ssh satelite 'mkdir -p /tmp/remote_debug_demo/data' +scp build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ + satelite:/tmp/remote_debug_demo/ +scp demos/lesson_2_4/remote_debug/data/*.txt satelite:/tmp/remote_debug_demo/data/ +``` + +Запустити на цільовому пристрої: + +```bash +ssh satelite +cd /tmp/remote_debug_demo +chmod +x debug_probe +./debug_probe data/good.txt +gdbserver :1234 ./debug_probe data/bad_missing_field.txt +``` + +Підключитись з комп'ютера/devcontainer. Використовується локальний +крос-скомпільований виконуваний файл з debug-символами: + +```bash +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe +``` + +Всередині GDB: + +```text +target remote satelite:1234 +continue +bt +frame 0 +print text +quit +``` + +## Core dump + +Локально: + +```bash +ulimit -c unlimited +./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ + demos/lesson_2_4/remote_debug/data/bad_missing_field.txt +gdb ./build/debug/demos/lesson_2_4/remote_debug/debug_probe core +``` + +На `satelite`: + +```bash +ssh satelite +cd /tmp/remote_debug_demo +ulimit -c unlimited +./debug_probe data/bad_missing_field.txt +ls -lh core* +``` + +Якщо core забрав systemd-coredump: + +```bash +coredumpctl list debug_probe +coredumpctl dump debug_probe > /tmp/remote_debug_demo/debug_probe.core +``` + +Скопіювати core на комп'ютер і відкрити через локальний ARM64 виконуваний файл +з debug-символами: + +```bash +scp 'satelite:/tmp/remote_debug_demo/core*' /tmp/ +CORE_FILE=$(ls -t /tmp/core* | head -1) +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ + "$CORE_FILE" +``` + +Альтернативно, якщо core було експортовано через `coredumpctl dump`: + +```bash +scp satelite:/tmp/remote_debug_demo/debug_probe.core /tmp/debug_probe.core +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ + /tmp/debug_probe.core +``` diff --git a/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt b/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt new file mode 100644 index 0000000..956941f --- /dev/null +++ b/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt @@ -0,0 +1 @@ +7 24.6 diff --git a/demos/lesson_2_4/remote_debug/data/good.txt b/demos/lesson_2_4/remote_debug/data/good.txt new file mode 100644 index 0000000..269f2db --- /dev/null +++ b/demos/lesson_2_4/remote_debug/data/good.txt @@ -0,0 +1 @@ +7 24.6 12 diff --git a/demos/lesson_2_4/remote_debug/src/main.cpp b/demos/lesson_2_4/remote_debug/src/main.cpp new file mode 100644 index 0000000..1932527 --- /dev/null +++ b/demos/lesson_2_4/remote_debug/src/main.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +// Lecture demo notes: +// this file intentionally contains two runtime defects. The defects are related +// to malformed input and heap ownership. Exact locations are not marked on +// purpose. + +const int EXPECTED_FIELD_COUNT = 3; +const int MAX_LINE_LENGTH = 128; + +struct ProbeSample { + int seq; + double battery_v; + int satellites; +}; + +int split_line(char line[], char* fields[], int max_fields) { + int count = 0; + char* cursor = line; + + while (*cursor != '\0' && count < max_fields) { + while (*cursor == ' ' || *cursor == '\t' || *cursor == '\n' || *cursor == '\r') { + *cursor = '\0'; + ++cursor; + } + + if (*cursor == '\0') { + break; + } + + fields[count] = cursor; + ++count; + + while (*cursor != '\0' && *cursor != ' ' && *cursor != '\t' && *cursor != '\n' && + *cursor != '\r') { + ++cursor; + } + } + + return count; +} + +int parse_int(const char* text) { + // This keeps parsing visibly simple for a GDB demo. + if (text[0] == '+') { + ++text; + } + + return static_cast(std::strtol(text, nullptr, 10)); +} + +double parse_double(const char* text) { + // This mirrors parse_int so both functions are easy to inspect in GDB. + if (text[0] == '+') { + ++text; + } + + return std::strtod(text, nullptr); +} + +ProbeSample parse_sample(char line[]) { + char* fields[EXPECTED_FIELD_COUNT] = {}; + const int field_count = split_line(line, fields, EXPECTED_FIELD_COUNT); + (void)field_count; + + ProbeSample sample{}; + sample.seq = parse_int(fields[0]); + sample.battery_v = parse_double(fields[1]); + sample.satellites = parse_int(fields[2]); + return sample; +} + +char* health_label(const ProbeSample& sample) { + char* label = new char[16]; + + if (sample.battery_v < 21.0) { + std::strcpy(label, "battery_low"); + return label; + } + + if (sample.satellites < 4) { + std::strcpy(label, "gps_weak"); + return label; + } + + std::strcpy(label, "ready"); + return label; +} + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "usage: debug_probe \n"; + return 1; + } + + std::ifstream input{argv[1]}; + if (!input) { + std::cerr << "error: failed to open input file: " << argv[1] << '\n'; + return 2; + } + + char line[MAX_LINE_LENGTH]; + if (!input.getline(line, MAX_LINE_LENGTH)) { + std::cerr << "error: input file is empty\n"; + return 3; + } + + const ProbeSample sample = parse_sample(line); + char* health = health_label(sample); + + std::cout << "seq " << sample.seq << '\n'; + std::cout << "battery_v " << sample.battery_v << '\n'; + std::cout << "satellites " << sample.satellites << '\n'; + std::cout << "health " << health << '\n'; + + return 0; +} From 3669355321a640ce70ae6e01d780559b54c9ebbf Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 16:29:11 +0200 Subject: [PATCH 41/49] feat(block-2-lesson-2-4): add homework 05 telemetry starter Block: 2 Lesson: 2.4 --- homework_05/README.md | 17 +++ homework_05/data/bad_invalid_number.txt | 3 + homework_05/data/bad_missing_field.txt | 3 + homework_05/data/bad_zero_delta.txt | 2 + homework_05/data/empty.txt | 1 + homework_05/data/good.txt | 4 + homework_05/include/telemetry.hpp | 35 ++++++ homework_05/src/main.cpp | 19 +++ homework_05/src/telemetry.cpp | 153 ++++++++++++++++++++++++ 9 files changed, 237 insertions(+) create mode 100644 homework_05/README.md create mode 100644 homework_05/data/bad_invalid_number.txt create mode 100644 homework_05/data/bad_missing_field.txt create mode 100644 homework_05/data/bad_zero_delta.txt create mode 100644 homework_05/data/empty.txt create mode 100644 homework_05/data/good.txt create mode 100644 homework_05/include/telemetry.hpp create mode 100644 homework_05/src/main.cpp create mode 100644 homework_05/src/telemetry.cpp diff --git a/homework_05/README.md b/homework_05/README.md new file mode 100644 index 0000000..ff68cdd --- /dev/null +++ b/homework_05/README.md @@ -0,0 +1,17 @@ +# ДЗ 5: стартовий код + +Ця директорія містить стартовий код для ДЗ 5. У коді навмисно залишено +помилки під час виконання. Код компілюється, але падає або аварійно +завершується на проблемних вхідних файлах. + +`CMakeLists.txt` не додано навмисно. Його потрібно створити як частину ДЗ: + +- `telemetry` - бібліотека з логікою читання, парсингу і підсумку; +- `telemetry_check` - виконуваний файл; +- `telemetry_check` лінкується до `telemetry`; +- `homework_05` підключається у кореневому `CMakeLists.txt`. + +Після виправлення `good.txt` має друкувати підсумок, а проблемні вхідні файли +не мають призводити до аварійного падіння. Для некоректних вхідних даних +очікується коротке повідомлення про помилку у `stderr` і ненульовий код +завершення. diff --git a/homework_05/data/bad_invalid_number.txt b/homework_05/data/bad_invalid_number.txt new file mode 100644 index 0000000..a85629c --- /dev/null +++ b/homework_05/data/bad_invalid_number.txt @@ -0,0 +1,3 @@ +0 1 24.8 3.1 41.0 1 14 +100 2 24.7 not_a_number 41.4 1 14 +200 3 24.6 3.3 41.8 1 13 diff --git a/homework_05/data/bad_missing_field.txt b/homework_05/data/bad_missing_field.txt new file mode 100644 index 0000000..e688f46 --- /dev/null +++ b/homework_05/data/bad_missing_field.txt @@ -0,0 +1,3 @@ +0 1 24.8 3.1 41.0 1 14 +100 2 24.7 3.2 41.4 1 +200 3 24.6 3.3 41.8 1 13 diff --git a/homework_05/data/bad_zero_delta.txt b/homework_05/data/bad_zero_delta.txt new file mode 100644 index 0000000..2b8f828 --- /dev/null +++ b/homework_05/data/bad_zero_delta.txt @@ -0,0 +1,2 @@ +0 1 24.8 3.1 41.0 1 14 +0 2 24.7 3.2 41.4 1 14 diff --git a/homework_05/data/empty.txt b/homework_05/data/empty.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/homework_05/data/empty.txt @@ -0,0 +1 @@ + diff --git a/homework_05/data/good.txt b/homework_05/data/good.txt new file mode 100644 index 0000000..e097051 --- /dev/null +++ b/homework_05/data/good.txt @@ -0,0 +1,4 @@ +0 1 24.8 3.1 41.0 1 14 +100 2 24.7 3.2 41.4 1 14 +200 3 24.6 3.3 41.8 1 13 +300 4 24.5 3.4 42.2 1 13 diff --git a/homework_05/include/telemetry.hpp b/homework_05/include/telemetry.hpp new file mode 100644 index 0000000..0cdf002 --- /dev/null +++ b/homework_05/include/telemetry.hpp @@ -0,0 +1,35 @@ +#pragma once + +// Fixed-size storage keeps the starter close to the topics from block 1. +const int MAX_TELEMETRY_FRAMES = 128; + +// One telemetry sample from the input log. +struct Frame { + long timestamp_ms; + int seq; + double voltage_v; + double current_a; + double temperature_c; + int gps_fix; + int satellites; +}; + +// Aggregated values printed by the executable. +struct Summary { + int frames_total; + int frames_valid; + double voltage_min; + double voltage_max; + double temperature_avg; + int low_voltage_frames; + double frame_rate_hz; +}; + +// Reads frames from a whitespace-separated telemetry log. +int read_frames(const char* path, Frame frames[], int max_frames); + +// Calculates summary values for already parsed frames. +Summary summarize(const Frame frames[], int frame_count); + +// Prints summary in the stable homework output format. +void print_summary(const Summary& summary); diff --git a/homework_05/src/main.cpp b/homework_05/src/main.cpp new file mode 100644 index 0000000..43ec461 --- /dev/null +++ b/homework_05/src/main.cpp @@ -0,0 +1,19 @@ +#include "telemetry.hpp" + +#include + +int main(int argc, char** argv) { + // The executable expects exactly one telemetry log path. + if (argc != 2) { + std::cerr << "usage: telemetry_check \n"; + return 1; + } + + Frame frames[MAX_TELEMETRY_FRAMES]; + const int frame_count = read_frames(argv[1], frames, MAX_TELEMETRY_FRAMES); + + const Summary summary = summarize(frames, frame_count); + print_summary(summary); + + return 0; +} diff --git a/homework_05/src/telemetry.cpp b/homework_05/src/telemetry.cpp new file mode 100644 index 0000000..9ac87fc --- /dev/null +++ b/homework_05/src/telemetry.cpp @@ -0,0 +1,153 @@ +#include "telemetry.hpp" + +#include +#include +#include + +// Debugging exercise notes: +// this file intentionally contains four runtime defects. +// The defects are related to malformed input shape, invalid numeric values, +// unsafe time deltas, and empty logs. Exact locations are not marked on purpose. + +const int EXPECTED_FIELD_COUNT = 7; +const int MAX_LINE_LENGTH = 256; + +int split_line(char line[], char* fields[], int max_fields) { + int count = 0; + char* cursor = line; + + while (*cursor != '\0' && count < max_fields) { + while (*cursor == ' ' || *cursor == '\t' || *cursor == '\n' || *cursor == '\r') { + *cursor = '\0'; + ++cursor; + } + + if (*cursor == '\0') { + break; + } + + fields[count] = cursor; + ++count; + + while (*cursor != '\0' && *cursor != ' ' && *cursor != '\t' && *cursor != '\n' && + *cursor != '\r') { + ++cursor; + } + } + + return count; +} + +long parse_long(const char* text) { + char* end = nullptr; + const long value = std::strtol(text, &end, 10); + + if (end == text) { + std::abort(); + } + + return value; +} + +int parse_int(const char* text) { + return static_cast(parse_long(text)); +} + +double parse_double(const char* text) { + char* end = nullptr; + const double value = std::strtod(text, &end); + + if (end == text) { + std::abort(); + } + + return value; +} + +Frame parse_frame(char line[]) { + char* fields[EXPECTED_FIELD_COUNT] = {}; + const int field_count = split_line(line, fields, EXPECTED_FIELD_COUNT); + (void)field_count; + + Frame frame{}; + frame.timestamp_ms = parse_long(fields[0]); + frame.seq = parse_int(fields[1]); + frame.voltage_v = parse_double(fields[2]); + frame.current_a = parse_double(fields[3]); + frame.temperature_c = parse_double(fields[4]); + frame.gps_fix = parse_int(fields[5]); + frame.satellites = parse_int(fields[6]); + return frame; +} + +double compute_frame_rate_hz(const Frame frames[], int frame_count) { + const long elapsed_ms = frames[frame_count - 1].timestamp_ms - frames[0].timestamp_ms; + + return static_cast((frame_count - 1) * 1000 / elapsed_ms); +} + +int read_frames(const char* path, Frame frames[], int max_frames) { + std::ifstream input{path}; + if (!input) { + std::cerr << "error: failed to open input file: " << path << '\n'; + return 0; + } + + int frame_count = 0; + char line[MAX_LINE_LENGTH]; + + while (input.getline(line, MAX_LINE_LENGTH)) { + if (line[0] == '\0') { + continue; + } + + if (frame_count < max_frames) { + frames[frame_count] = parse_frame(line); + ++frame_count; + } + } + + return frame_count; +} + +Summary summarize(const Frame frames[], int frame_count) { + Summary summary{}; + summary.frames_total = frame_count; + summary.frames_valid = frame_count; + summary.voltage_min = frames[0].voltage_v; + summary.voltage_max = frames[0].voltage_v; + summary.low_voltage_frames = 0; + + double temperature_sum = 0.0; + + for (int i = 0; i < frame_count; ++i) { + if (frames[i].voltage_v < summary.voltage_min) { + summary.voltage_min = frames[i].voltage_v; + } + + if (frames[i].voltage_v > summary.voltage_max) { + summary.voltage_max = frames[i].voltage_v; + } + + temperature_sum += frames[i].temperature_c; + + if (frames[i].voltage_v < 22.0) { + ++summary.low_voltage_frames; + } + } + + const int temperature_tenths = static_cast(temperature_sum * 10.0) / frame_count; + summary.temperature_avg = static_cast(temperature_tenths) / 10.0; + summary.frame_rate_hz = compute_frame_rate_hz(frames, frame_count); + return summary; +} + +void print_summary(const Summary& summary) { + std::cout << "frames_total " << summary.frames_total << '\n'; + std::cout << "frames_valid " << summary.frames_valid << '\n'; + std::cout << "voltage_min " << summary.voltage_min << '\n'; + std::cout << "voltage_max " << summary.voltage_max << '\n'; + std::cout << "temperature_avg " << summary.temperature_avg << '\n'; + std::cout << "low_voltage_frames " << summary.low_voltage_frames << '\n'; + std::cout << "frame_rate_hz " << summary.frame_rate_hz << '\n'; +} From 1dfb382d1f73ddadad297822dda7b4d0a8ca3dc3 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 16:29:15 +0200 Subject: [PATCH 42/49] chore(block-2-lesson-2-4): add debug presets and launch tooling Block: 2 Lesson: 2.4 --- .devcontainer/apt-packages.in | 3 ++ .devcontainer/devcontainer.json | 4 +- .github/workflows/build.yml | 13 +++--- .vscode/launch.json | 56 ++++++++++++++++++++++++ .vscode/tasks.json | 4 +- CMakePresets.json | 45 +++++++++++++++++++ cmake/toolchains/aarch64-linux-gnu.cmake | 15 +++++++ 7 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 CMakePresets.json create mode 100644 cmake/toolchains/aarch64-linux-gnu.cmake diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index e6e4367..0ddf7ae 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -13,6 +13,9 @@ clang clangd clang-tidy +# Крос-тулчейн для ARM64-пристроїв: RPi4, Radxa ROCK 5B+, Jetson. +g++-aarch64-linux-gnu + # Інструменти для заняття 2.4: локальний і remote debug, memory diagnostics. gdb gdbserver diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 79a80a8..d6df87f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -53,10 +53,10 @@ "llvm-vs-code-extensions.vscode-clangd" ], "settings": { - // clangd читає build/compile_commands.json, який генерується CMake. + // clangd читає build/debug/compile_commands.json з debug preset. "clangd.path": "/usr/bin/clangd", "clangd.arguments": [ - "--compile-commands-dir=build", + "--compile-commands-dir=build/debug", "--clang-tidy", "--background-index" ], diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bb8d39..33032ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,9 +39,10 @@ jobs: # 4. Монтує checkout у workspaceFolder. # 5. Виконує runCmd від імені remoteUser. # - # -G Ninja: явно обираємо Ninja-генератор. ninja-build уже є у - # devcontainer-і, швидший за Make на проєктах з багатьма файлами - # (кращий паралелізм, менше overhead-у на запуск shell-команд). + # CMake preset debug тримає локальну і CI-збірку в одному місці: + # build/debug. Це також збігається з clangd compile_commands і + # VS Code debug launch config. ARM64 preset додатково перевіряє, + # що demo-код можна крос-компілювати для SBC target-а. - name: Build inside dev container uses: devcontainers/ci@v0.3 with: @@ -51,5 +52,7 @@ jobs: # на runner-і для runCmd. push: never runCmd: | - cmake -S . -B build -G Ninja - cmake --build build + cmake --preset debug + cmake --build --preset debug + cmake --preset aarch64-debug + cmake --build --preset aarch64-debug diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c0e43fb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,56 @@ +{ + // Launch configurations працюють у devcontainer і використовують GDB з image. + // Для ДЗ 5 потрібно буде додати окрему конфігурацію за цим прикладом. + "version": "0.2.0", + "configurations": [ + { + // Локальний debug demo-коду заняття 2.4. Використовує bad input, + // щоб GDB зупинився на runtime crash. + "name": "Debug demo 2.4: remote_debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/demos/lesson_2_4/remote_debug/debug_probe", + "args": [ + "${workspaceFolder}/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "CMake: configure and build", + "setupCommands": [ + { + "description": "Увімкнути pretty-printing для STL типів у GDB.", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + // Локальний debug стартера ДЗ 4. preLaunchTask збирає debug preset. + "name": "Debug homework_04: ugv_odometry", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/homework_04/ugv_odometry", + "args": [ + "${workspaceFolder}/homework_04/data/straight.txt" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "CMake: configure and build", + "setupCommands": [ + { + "description": "Увімкнути pretty-printing для STL типів у GDB.", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aabd11f..a29b2ed 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,10 +4,10 @@ "version": "2.0.0", "tasks": [ { - // Default build task: Ctrl+Shift+B запускає CMake configure, потім build. + // Default build task: Ctrl+Shift+B запускає debug preset, потім build. "label": "CMake: configure and build", "type": "shell", - "command": "cmake -S . -B build -G Ninja && cmake --build build", + "command": "cmake --preset debug && cmake --build --preset debug", "group": { "kind": "build", "isDefault": true diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..f7f4f45 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,45 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "debug", + "displayName": "Debug", + "description": "Debug build для занять блоку 2: символи для GDB і зрозуміла структура build/debug.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "aarch64-debug", + "displayName": "ARM64 Debug", + "description": "Cross-compile debug build для Raspberry Pi / Radxa / Jetson через aarch64-linux-gnu toolchain.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/aarch64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/toolchains/aarch64-linux-gnu.cmake" + } + } + ], + "buildPresets": [ + { + "name": "debug", + "displayName": "Build Debug", + "description": "Зібрати debug preset після configure.", + "configurePreset": "debug" + }, + { + "name": "aarch64-debug", + "displayName": "Build ARM64 Debug", + "description": "Зібрати ARM64 debug preset після cross-configure.", + "configurePreset": "aarch64-debug" + } + ] +} diff --git a/cmake/toolchains/aarch64-linux-gnu.cmake b/cmake/toolchains/aarch64-linux-gnu.cmake new file mode 100644 index 0000000..04a9130 --- /dev/null +++ b/cmake/toolchains/aarch64-linux-gnu.cmake @@ -0,0 +1,15 @@ +# Toolchain для cross-compilation з Linux x86_64 devcontainer-а під ARM64 Linux. +# Підходить для Raspberry Pi 4, Radxa ROCK 5B+ і Jetson з 64-bit Linux userspace. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +# Компілятори з Debian/Ubuntu пакета g++-aarch64-linux-gnu. +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) + +# Пошук headers/libraries йде у target sysroot, а host-програм - на host-і. +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) From 501d0c9a3622dbbba5b0fc7e6cf06357fc64ec54 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 16:29:19 +0200 Subject: [PATCH 43/49] docs(block-2-lesson-2-4): document debug demo and homework starter Block: 2 Lesson: 2.4 --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 24 +++++++++++++++++++----- preps/README.md | 6 +++--- preps/devcontainers-cli.md | 4 ++-- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e07dd..e6f4b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ ### Added +- Block 2 / Lesson 2.4: `debug` CMake preset, VS Code launch config для + `homework_04` і build task, який використовує Debug-збірку через preset. +- Block 2 / Lessons 2.4-2.5: стартовий код `homework_05` для діагностики + телеметрії з C++ бібліотекою, виконуваним файлом, проблемними вхідними + файлами і навмисними помилками під час виконання. + `CMakeLists.txt` для `homework_05` не додано навмисно, це частина ДЗ 5. +- Block 2 / Lesson 2.4: demo `demos/lesson_2_4/remote_debug` для локального + консольного GDB, core dump, Valgrind і віддаленого GDB/gdbserver на + Raspberry Pi / ARM64-пристрої. +- Block 2 / Lesson 2.4: ARM64 cross-compilation preset `aarch64-debug` і + CMake toolchain `cmake/toolchains/aarch64-linux-gnu.cmake`. - Block 2 / Lessons 2.3-2.4: VS Code tasks для CMake configure/build і видалення локальної `build/` директорії. `CMake: configure and build` зроблено default build task для `Ctrl+Shift+B`. @@ -22,6 +33,12 @@ додано пояснювальні коментарі до student-facing tooling/config файлів і зменшено `.gitignore` до мінімального C++/CMake набору. ([#9](https://github.com/robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY/pull/9)) +- Block 2 / Lesson 2.4: clangd тепер читає compile database з + `build/debug/compile_commands.json`, щоб відповідати `debug` preset. +- Block 2 / Lesson 2.4: README/preps і CI build flow переведено на + `cmake --preset debug` і `cmake --build --preset debug`. +- Block 2 / Lesson 2.4: CI build flow додатково перевіряє + `aarch64-debug` cross-build. ## 2026-04-25 diff --git a/README.md b/README.md index 806dfa2..a46cd9a 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,19 @@ PR-и на вашій копії йдуть у ваш main. 3. **Зібрати весь репо всередині контейнера:** ```bash - cmake -S . -B build -G Ninja - cmake --build build + cmake --preset debug + cmake --build --preset debug ``` - Виконувані файли домашніх робіт з'являться в `build/homework_XX/`. + Виконувані файли домашніх робіт з'являться в `build/debug/homework_XX/`. + Demo-код занять лежить у відповідних піддиректоріях `build/debug/demos/`. + +4. **Зібрати ARM64 debug binary для Raspberry Pi / Radxa / Jetson:** + ```bash + cmake --preset aarch64-debug + cmake --build --preset aarch64-debug + ``` + ARM64 артефакти з'являться в `build/aarch64-debug/`. Цей preset + використовує toolchain `cmake/toolchains/aarch64-linux-gnu.cmake`. ## Доступ для перевірки @@ -102,6 +111,8 @@ Template. Копія через "Use this template" - це новий репо ├── .github/workflows/ # GitHub Actions: CI build через devcontainer ├── CHANGELOG.md # лог помітних змін по PR-ах ├── CMakeLists.txt # корневий CMakeLists, підтягує homework_XX через add_subdirectory +├── cmake/toolchains/ # CMake toolchain-файли для cross-compilation +├── demos/ # demo-код для занять ├── homework_XX/ # окрема домашка, кожна зі своїм CMakeLists.txt └── preps/ # інструкції зі сетапу за платформами ``` @@ -124,8 +135,11 @@ Template. Копія через "Use this template" - це новий репо Workflow `.github/workflows/build.yml` запускається на кожен PR (і на push у main). Він: 1. Будує devcontainer image з Dockerfile. -2. Виконує всередині `cmake -S . -B build -G Ninja && cmake --build build`. -3. Падає якщо хоч одна `homework_XX` не компілюється з +2. Виконує native debug build: + `cmake --preset debug && cmake --build --preset debug`. +3. Виконує ARM64 cross debug build: + `cmake --preset aarch64-debug && cmake --build --preset aarch64-debug`. +4. Падає якщо хоч одна `homework_XX` або demo target не компілюється з тулчейном з devcontainer-а (gcc-13, clang-18, C++20). Якщо CI червоний локально зібралося, але на CI ні - швидше за все diff --git a/preps/README.md b/preps/README.md index b6da9b0..2bb012f 100644 --- a/preps/README.md +++ b/preps/README.md @@ -38,9 +38,9 @@ git pull Всередині контейнера (VS Code terminal): ```bash -cmake -S . -B build -G Ninja -cmake --build build -./build/homework_04/ugv_odometry homework_04/data/straight.txt +cmake --preset debug +cmake --build --preset debug +./build/debug/homework_04/ugv_odometry homework_04/data/straight.txt ``` Перші дві команди мають завершитись без помилки. Остання команда запускає diff --git a/preps/devcontainers-cli.md b/preps/devcontainers-cli.md index 1754da9..4acfd70 100644 --- a/preps/devcontainers-cli.md +++ b/preps/devcontainers-cli.md @@ -26,8 +26,8 @@ devcontainer exec --workspace-folder . bash # зайти в контейне Одна команда без входу в shell: ```bash -devcontainer exec --workspace-folder . cmake -S . -B build -devcontainer exec --workspace-folder . cmake --build build +devcontainer exec --workspace-folder . cmake --preset debug +devcontainer exec --workspace-folder . cmake --build --preset debug ``` Після зміни `Dockerfile`: From b901eeeca3c433c27a842694fb01951e9b31adb2 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 16:42:05 +0200 Subject: [PATCH 44/49] fix(block-2-lesson-2-4): align debug_probe demo naming Block: 2 Lesson: 2.4 --- .vscode/launch.json | 6 +- CHANGELOG.md | 2 +- CMakeLists.txt | 4 +- .../CMakeLists.txt | 4 +- .../{remote_debug => debug_probe}/README.md | 56 +++++++++---------- .../data/bad_missing_field.txt | 0 .../data/good.txt | 0 .../src/main.cpp | 0 8 files changed, 36 insertions(+), 36 deletions(-) rename demos/lesson_2_4/{remote_debug => debug_probe}/CMakeLists.txt (81%) rename demos/lesson_2_4/{remote_debug => debug_probe}/README.md (65%) rename demos/lesson_2_4/{remote_debug => debug_probe}/data/bad_missing_field.txt (100%) rename demos/lesson_2_4/{remote_debug => debug_probe}/data/good.txt (100%) rename demos/lesson_2_4/{remote_debug => debug_probe}/src/main.cpp (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index c0e43fb..7e724e0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,12 +6,12 @@ { // Локальний debug demo-коду заняття 2.4. Використовує bad input, // щоб GDB зупинився на runtime crash. - "name": "Debug demo 2.4: remote_debug", + "name": "Debug demo 2.4: debug_probe", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/debug/demos/lesson_2_4/remote_debug/debug_probe", + "program": "${workspaceFolder}/build/debug/demos/lesson_2_4/debug_probe/debug_probe", "args": [ - "${workspaceFolder}/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt" + "${workspaceFolder}/demos/lesson_2_4/debug_probe/data/bad_missing_field.txt" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/CHANGELOG.md b/CHANGELOG.md index e6f4b5e..3f9dbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ телеметрії з C++ бібліотекою, виконуваним файлом, проблемними вхідними файлами і навмисними помилками під час виконання. `CMakeLists.txt` для `homework_05` не додано навмисно, це частина ДЗ 5. -- Block 2 / Lesson 2.4: demo `demos/lesson_2_4/remote_debug` для локального +- Block 2 / Lesson 2.4: demo `demos/lesson_2_4/debug_probe` для локального консольного GDB, core dump, Valgrind і віддаленого GDB/gdbserver на Raspberry Pi / ARM64-пристрої. - Block 2 / Lesson 2.4: ARM64 cross-compilation preset `aarch64-debug` і diff --git a/CMakeLists.txt b/CMakeLists.txt index bc656f5..79f396b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,5 +17,5 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) -# Demo-код для заняття 2.4: локальний і remote debug через GDB. -add_subdirectory(demos/lesson_2_4/remote_debug) +# Demo-код для заняття 2.4: локальне і віддалене відлагодження через GDB. +add_subdirectory(demos/lesson_2_4/debug_probe) diff --git a/demos/lesson_2_4/remote_debug/CMakeLists.txt b/demos/lesson_2_4/debug_probe/CMakeLists.txt similarity index 81% rename from demos/lesson_2_4/remote_debug/CMakeLists.txt rename to demos/lesson_2_4/debug_probe/CMakeLists.txt index 64ea084..f141f45 100644 --- a/demos/lesson_2_4/remote_debug/CMakeLists.txt +++ b/demos/lesson_2_4/debug_probe/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.20) # Цей CMakeLists можна збирати двома способами: # - як частину кореневого репозиторію через add_subdirectory(); -# - окремо на target-пристрої, якщо скопійовано лише цю demo-директорію. +# - окремо на цільовому пристрої, якщо скопійовано лише цю demo-директорію. if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - project(remote_debug_demo LANGUAGES CXX) + project(debug_probe LANGUAGES CXX) # Standalone-збірка використовує той самий C++20, що й основний курс. set(CMAKE_CXX_STANDARD 20) diff --git a/demos/lesson_2_4/remote_debug/README.md b/demos/lesson_2_4/debug_probe/README.md similarity index 65% rename from demos/lesson_2_4/remote_debug/README.md rename to demos/lesson_2_4/debug_probe/README.md index a5b5f74..4718199 100644 --- a/demos/lesson_2_4/remote_debug/README.md +++ b/demos/lesson_2_4/debug_probe/README.md @@ -1,4 +1,4 @@ -# Демо 2.4: локальний і віддалений GDB +# Демо 2.4: debug_probe для локального і віддаленого GDB Цей приклад використовується на занятті 2.4 для показу одного циклу відлагодження: @@ -18,8 +18,8 @@ cmake --preset debug cmake --build --preset debug -./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ - demos/lesson_2_4/remote_debug/data/good.txt +./build/debug/demos/lesson_2_4/debug_probe/debug_probe \ + demos/lesson_2_4/debug_probe/data/good.txt ``` Очікуваний вивід: @@ -34,13 +34,13 @@ health ready ## Консольний GDB ```bash -gdb ./build/debug/demos/lesson_2_4/remote_debug/debug_probe +gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe ``` Всередині GDB: ```text -run demos/lesson_2_4/remote_debug/data/bad_missing_field.txt +run demos/lesson_2_4/debug_probe/data/bad_missing_field.txt bt frame 0 print text @@ -54,8 +54,8 @@ quit ```bash valgrind --leak-check=full \ - ./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ - demos/lesson_2_4/remote_debug/data/good.txt + ./build/debug/demos/lesson_2_4/debug_probe/debug_probe \ + demos/lesson_2_4/debug_probe/data/good.txt ``` Очікуваний сигнал: `definitely lost` memory block. @@ -65,11 +65,11 @@ valgrind --leak-check=full \ ```bash valgrind \ - ./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ - demos/lesson_2_4/remote_debug/data/bad_missing_field.txt + ./build/debug/demos/lesson_2_4/debug_probe/debug_probe \ + demos/lesson_2_4/debug_probe/data/bad_missing_field.txt ``` -## Cross build для Raspberry Pi +## Крос-збірка для Raspberry Pi ```bash cmake --preset aarch64-debug @@ -79,10 +79,10 @@ cmake --build --preset aarch64-debug ARM64 виконуваний файл: ```text -build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe +build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe ``` -## Remote run на Raspberry Pi +## Віддалений запуск на Raspberry Pi На `satelite` потрібен `gdbserver`, але не компілятор: @@ -96,18 +96,18 @@ exit Скопіювати виконуваний файл і дані: ```bash -ssh satelite 'rm -rf /tmp/remote_debug_demo' -ssh satelite 'mkdir -p /tmp/remote_debug_demo/data' -scp build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ - satelite:/tmp/remote_debug_demo/ -scp demos/lesson_2_4/remote_debug/data/*.txt satelite:/tmp/remote_debug_demo/data/ +ssh satelite 'rm -rf /tmp/debug_probe' +ssh satelite 'mkdir -p /tmp/debug_probe/data' +scp build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe \ + satelite:/tmp/debug_probe/ +scp demos/lesson_2_4/debug_probe/data/*.txt satelite:/tmp/debug_probe/data/ ``` Запустити на цільовому пристрої: ```bash ssh satelite -cd /tmp/remote_debug_demo +cd /tmp/debug_probe chmod +x debug_probe ./debug_probe data/good.txt gdbserver :1234 ./debug_probe data/bad_missing_field.txt @@ -117,7 +117,7 @@ gdbserver :1234 ./debug_probe data/bad_missing_field.txt крос-скомпільований виконуваний файл з debug-символами: ```bash -gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe ``` Всередині GDB: @@ -137,16 +137,16 @@ quit ```bash ulimit -c unlimited -./build/debug/demos/lesson_2_4/remote_debug/debug_probe \ - demos/lesson_2_4/remote_debug/data/bad_missing_field.txt -gdb ./build/debug/demos/lesson_2_4/remote_debug/debug_probe core +./build/debug/demos/lesson_2_4/debug_probe/debug_probe \ + demos/lesson_2_4/debug_probe/data/bad_missing_field.txt +gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe core ``` На `satelite`: ```bash ssh satelite -cd /tmp/remote_debug_demo +cd /tmp/debug_probe ulimit -c unlimited ./debug_probe data/bad_missing_field.txt ls -lh core* @@ -156,23 +156,23 @@ ls -lh core* ```bash coredumpctl list debug_probe -coredumpctl dump debug_probe > /tmp/remote_debug_demo/debug_probe.core +coredumpctl dump debug_probe > /tmp/debug_probe/debug_probe.core ``` Скопіювати core на комп'ютер і відкрити через локальний ARM64 виконуваний файл з debug-символами: ```bash -scp 'satelite:/tmp/remote_debug_demo/core*' /tmp/ +scp 'satelite:/tmp/debug_probe/core*' /tmp/ CORE_FILE=$(ls -t /tmp/core* | head -1) -gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe \ "$CORE_FILE" ``` Альтернативно, якщо core було експортовано через `coredumpctl dump`: ```bash -scp satelite:/tmp/remote_debug_demo/debug_probe.core /tmp/debug_probe.core -gdb-multiarch build/aarch64-debug/demos/lesson_2_4/remote_debug/debug_probe \ +scp satelite:/tmp/debug_probe/debug_probe.core /tmp/debug_probe.core +gdb-multiarch build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe \ /tmp/debug_probe.core ``` diff --git a/demos/lesson_2_4/remote_debug/data/bad_missing_field.txt b/demos/lesson_2_4/debug_probe/data/bad_missing_field.txt similarity index 100% rename from demos/lesson_2_4/remote_debug/data/bad_missing_field.txt rename to demos/lesson_2_4/debug_probe/data/bad_missing_field.txt diff --git a/demos/lesson_2_4/remote_debug/data/good.txt b/demos/lesson_2_4/debug_probe/data/good.txt similarity index 100% rename from demos/lesson_2_4/remote_debug/data/good.txt rename to demos/lesson_2_4/debug_probe/data/good.txt diff --git a/demos/lesson_2_4/remote_debug/src/main.cpp b/demos/lesson_2_4/debug_probe/src/main.cpp similarity index 100% rename from demos/lesson_2_4/remote_debug/src/main.cpp rename to demos/lesson_2_4/debug_probe/src/main.cpp From 2f9f8d5e7c14d76597b76859c67e5a8fcfb49e2d Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 17:03:56 +0200 Subject: [PATCH 45/49] chore(block-2-lesson-2-4): enable core dump debugging in devcontainer Block: 2 Lesson: 2.4 --- .devcontainer/devcontainer.json | 8 +++ CHANGELOG.md | 11 ++++ demos/lesson_2_4/debug_probe/README.md | 79 +++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d6df87f..6e4e573 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -34,8 +34,16 @@ ], // host network спрощує SSH/debug сценарії для занять з target-пристроями. + // SYS_PTRACE і seccomp=unconfined потрібні для стабільної роботи GDB + // всередині контейнера, особливо коли debug-сесія attach-иться до процесу. + // ulimit core=-1 дозволяє процесам у контейнері створювати core dump. + // Сам шлях/формат core-файла все одно визначає host kernel через + // /proc/sys/kernel/core_pattern; у WSL це налаштовується окремо на host. "runArgs": [ "--network=host", + "--cap-add=SYS_PTRACE", + "--security-opt=seccomp=unconfined", + "--ulimit=core=-1", "--hostname=devcontainer", "--add-host=devcontainer:127.0.1.1" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f9dbf5..5e10f18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,17 @@ `cmake --preset debug` і `cmake --build --preset debug`. - Block 2 / Lesson 2.4: CI build flow додатково перевіряє `aarch64-debug` cross-build. +- Block 2 / Lesson 2.4: remote GDB інструкції уточнено: `satelite` лишається + SSH alias, а `target remote` використовує IP пристрою. +- Block 2 / Lesson 2.4: core dump інструкції доповнено fallback-сценарієм для + WSL/Docker, де core-файл може перехоплюватись системним handler-ом. +- Block 2 / Lesson 2.4: devcontainer runtime args доповнено `SYS_PTRACE`, + `seccomp=unconfined` і `core=-1` для GDB/core dump сценаріїв. +- Block 2 / Lesson 2.4: core dump README доповнено WSL host setup через + `kernel.core_pattern`, перевіркою `core.%e.%p` після rebuild/reopen + devcontainer і запуском GDB по фактичному `core*` файлу. +- Block 2 / Lesson 2.4: core dump README уточнює, що `ulimit -c unlimited` + потрібно виконати у поточній shell-сесії, якщо перевірка показує `0`. ## 2026-04-25 diff --git a/demos/lesson_2_4/debug_probe/README.md b/demos/lesson_2_4/debug_probe/README.md index 4718199..b933406 100644 --- a/demos/lesson_2_4/debug_probe/README.md +++ b/demos/lesson_2_4/debug_probe/README.md @@ -123,7 +123,7 @@ gdb-multiarch build/aarch64-debug/demos/lesson_2_4/debug_probe/debug_probe Всередині GDB: ```text -target remote satelite:1234 +target remote 192.168.1.12:1234 continue bt frame 0 @@ -131,15 +131,90 @@ print text quit ``` +`satelite` - це SSH alias. GDB підключається не через SSH config, а через +звичайний TCP host:port, тому для `target remote` потрібен IP або DNS-ім'я +пристрою. + ## Core dump +Devcontainer для цього репозиторію запускається з runtime args для +відлагодження: + +```text +--cap-add=SYS_PTRACE +--security-opt=seccomp=unconfined +--ulimit=core=-1 +``` + +Після зміни цих параметрів контейнер потрібно перестворити. Вони дають GDB +потрібні права і прибирають ліміт на розмір core dump, але не змінюють +політику host kernel. + +На WSL host перед demo можна тимчасово задати простий шаблон core-файлів: + +```bash +sudo sysctl -w kernel.core_pattern='core.%e.%p' +``` + +Очікувана перевірка на WSL host: + +```bash +cat /proc/sys/kernel/core_pattern +# core.%e.%p +``` + +Після цього потрібно перестворити або заново відкрити devcontainer, щоб +застосувались `runArgs` з `.devcontainer/devcontainer.json`. Усередині +devcontainer перевірка має показати: + +```bash +ulimit -c +# unlimited + +cat /proc/sys/kernel/core_pattern +# core.%e.%p +``` + +Якщо `ulimit -c` показує `0`, увімкнути core dump для поточного термінала: + +```bash +ulimit -c unlimited +ulimit -c +``` + +Це налаштування діє тільки для поточної shell-сесії та процесів, запущених з неї. + +Після перезапуску WSL це налаштування може повернутись до стандартного +`wsl-capture-crash`. + Локально: ```bash ulimit -c unlimited ./build/debug/demos/lesson_2_4/debug_probe/debug_probe \ demos/lesson_2_4/debug_probe/data/bad_missing_field.txt -gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe core + +CORE_FILE=$(ls -t core* | head -1) +gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe "$CORE_FILE" +``` + +Якщо файл `core` не з'явився, перевірити політику системи: + +```bash +cat /proc/sys/kernel/core_pattern +``` + +На WSL/Docker crash може перехоплюватись системним handler-ом. Для стабільного +локального demo можна створити core-файл із GDB після зупинки на SIGSEGV: + +```bash +gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe +run demos/lesson_2_4/debug_probe/data/bad_missing_field.txt +generate-core-file /tmp/debug_probe.core +quit + +gdb ./build/debug/demos/lesson_2_4/debug_probe/debug_probe /tmp/debug_probe.core +bt ``` На `satelite`: From c9ef455a43876e66e8d74e5d89feaca4978ba22e Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Thu, 30 Apr 2026 17:20:50 +0200 Subject: [PATCH 46/49] fix(block-2-lesson-2-4): add clang sanitizer runtime to devcontainer Block: 2 Lesson: 2.4 --- .devcontainer/apt-packages.in | 4 ++++ .gitignore | 2 ++ CHANGELOG.md | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/.devcontainer/apt-packages.in b/.devcontainer/apt-packages.in index 0ddf7ae..e55eca6 100644 --- a/.devcontainer/apt-packages.in +++ b/.devcontainer/apt-packages.in @@ -13,6 +13,10 @@ clang clangd clang-tidy +# Runtime-бібліотеки Clang sanitizer-ів і symbolizer для читабельного ASan/UBSan output. +libclang-rt-18-dev +llvm-18 + # Крос-тулчейн для ARM64-пристроїв: RPi4, Radxa ROCK 5B+, Jetson. g++-aarch64-linux-gnu diff --git a/.gitignore b/.gitignore index 8d16481..530bb39 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ _deps/ *.out *.dSYM/ *.pdb +core +core.* # Метадані операційної системи не є частиною навчального репозиторію. .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e10f18..d87860d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,11 @@ devcontainer і запуском GDB по фактичному `core*` файлу. - Block 2 / Lesson 2.4: core dump README уточнює, що `ulimit -c unlimited` потрібно виконати у поточній shell-сесії, якщо перевірка показує `0`. +- Block 2 / Lesson 2.4: devcontainer отримав `libclang-rt-18-dev` і + `llvm-18`, щоб ASan/UBSan збірка через Clang лінкувалась і показувала + читабельний stack trace з file:line. +- Block 2 / Lesson 2.4: `.gitignore` ігнорує локальні `core` / `core.*` + файли, які з'являються під час core dump demo. ## 2026-04-25 From 92611ef60eb4e512ee3c0a16cda091f14dae520b Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Fri, 1 May 2026 00:34:16 +0200 Subject: [PATCH 47/49] fix(block-2-lesson-2-4): adjust devcontainer diagnostics setup Block: 2 Lesson: 2.4 --- .devcontainer/.clangd | 7 +++++++ .devcontainer/devcontainer.json | 5 ++++- CHANGELOG.md | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.devcontainer/.clangd b/.devcontainer/.clangd index e12fb3a..d417c24 100644 --- a/.devcontainer/.clangd +++ b/.devcontainer/.clangd @@ -8,3 +8,10 @@ InlayHints: ParameterNames: No # Типи, виведені через auto, корисно бачити під час навчання C++. DeducedTypes: Yes + +# clang-tidy конфіг лежить у repo/container, але diagnostics вимкнено до +# заняття 2.6, де static analysis буде окремою темою. +Diagnostics: + ClangTidy: + Remove: + - '*' diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6e4e573..04fae48 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -29,7 +29,9 @@ ], // initialize створює host-side директорії для mounts до старту container-а. + // Запуск через bash робить команду стійкою до копіювання без executable bit. "initializeCommand": [ + "bash", ".devcontainer/scripts/initialize" ], @@ -62,10 +64,11 @@ ], "settings": { // clangd читає build/debug/compile_commands.json з debug preset. + // clang-tidy конфіг лишається в репозиторії, але автоматичний запуск + // через clangd буде увімкнено пізніше, після заняття 2.6. "clangd.path": "/usr/bin/clangd", "clangd.arguments": [ "--compile-commands-dir=build/debug", - "--clang-tidy", "--background-index" ], "clangd.restartAfterCrash": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index d87860d..2d94649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,12 @@ читабельний stack trace з file:line. - Block 2 / Lesson 2.4: `.gitignore` ігнорує локальні `core` / `core.*` файли, які з'являються під час core dump demo. +- Block 2 / Lesson 2.4: clang-tidy diagnostics у clangd вимкнено до заняття + 2.6 через devcontainer settings і `.clangd`; конфіг і пакет clang-tidy + лишаються в repo/container для майбутнього ввімкнення. +- Block 2 / Lesson 2.4: `initializeCommand` запускає + `.devcontainer/scripts/initialize` через `bash`, щоб копіювання файлів без + executable bit не ламало старт devcontainer. ## 2026-04-25 From 93f819e249a5ef874c11eb8360515cef2d886e36 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Fri, 1 May 2026 01:17:29 +0200 Subject: [PATCH 48/49] docs(block-2-lesson-2-4): document course update sync flow Block: 2 Lesson: 2.4 --- CHANGELOG.md | 3 +++ README.md | 31 ++++++++++++++++++++++++++++--- preps/README.md | 30 +++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d94649..7137ede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,9 @@ - Block 2 / Lesson 2.4: `initializeCommand` запускає `.devcontainer/scripts/initialize` через `bash`, щоб копіювання файлів без executable bit не ламало старт devcontainer. +- Block 2 / Lesson 2.4: README/preps доповнено snapshot sync flow через + `git clone` + `tar`, `git status` і commit, щоб оновлення курс-репо не + залежали від ручного списку файлів. ## 2026-04-25 diff --git a/README.md b/README.md index a46cd9a..acd8d37 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,32 @@ formal review (approve / request changes) навіть на публічному ## Оновлення з курс-репо Зміни в курс-репо не синкаються автоматично у локальний репо. Якщо -щось важливе оновлюється - буде анонс у Slack-каналі курсу з -інструкцією, що замінити вручну. +щось важливе оновлюється - буде анонс у Slack-каналі курсу. + +Рекомендований спосіб підтягнути snapshot курс-репо без затирання ДЗ 4: + +```bash +tmp_dir=$(mktemp -d) + +git clone --depth 1 \ + git@github.com:robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git \ + "$tmp_dir/course" + +( + cd "$tmp_dir/course" + tar --exclude='./.git' --exclude='./homework_04' -cf - . +) | tar -xf - + +rm -rf "$tmp_dir" + +git status +git add . +git commit -m "chore: sync course repository updates" +``` + +Команда копіює tooling/devcontainer/CMake/demo/homework_05 з актуального +курс-репо, але не затирає `homework_04/`. `git status` перед комітом потрібен +як контроль того, які файли зміняться у локальному репо. **Чому не git merge / cherry-pick:** курс-репо позначений як GitHub Template. Копія через "Use this template" - це новий репо з єдиним @@ -85,7 +109,8 @@ Template. Копія через "Use this template" - це новий репо кореневі коміти і конфлікти на більшості файлів. Cherry-pick не падає сам, але конфліктує на локально модифікованих файлах. -Простіше - ручна заміна файлів за Slack-анонсом. Без git-акробатики. +Простіше - snapshot copy за командою вище. Без merge unrelated histories і +без ручного вибору окремих файлів. ## Перед стартом diff --git a/preps/README.md b/preps/README.md index 2bb012f..8914ca9 100644 --- a/preps/README.md +++ b/preps/README.md @@ -13,9 +13,33 @@ ## Якщо вже клонували репо раніше -Якщо репо клоновано до нових оновлень - дочекатися Slack-анонсу від лектора -і замінити вказані файли вручну. Курс-репо створене як GitHub Template, тому -звичайний `git pull` з upstream не працює надійно для студентських копій. +Якщо репо клоновано до нових оновлень - дочекатися Slack-анонсу від лектора. +Курс-репо створене як GitHub Template, тому звичайний `git pull` з upstream +не працює надійно для студентських копій. + +Для підтягування snapshot-а курс-репо без затирання ДЗ 4: + +```bash +tmp_dir=$(mktemp -d) + +git clone --depth 1 \ + git@github.com:robot-dreams-code/C-PLUS-PLUS-FOR-MILITARY-TECHNOLOGY.git \ + "$tmp_dir/course" + +( + cd "$tmp_dir/course" + tar --exclude='./.git' --exclude='./homework_04' -cf - . +) | tar -xf - + +rm -rf "$tmp_dir" + +git status +git add . +git commit -m "chore: sync course repository updates" +``` + +`git status` перед комітом потрібен як перевірка, що `homework_04/` не +затерто, а оновлення tooling/devcontainer/CMake/demo/homework_05 підтягнуто. Якщо клонована саме власна копія репозиторію, локальні зміни з неї підтягуються як звичайно: From 57e751dbba6a8c28b7dd621ba6cb9c892bd20f42 Mon Sep 17 00:00:00 2001 From: Yevhen Kuznetsov Date: Fri, 8 May 2026 16:25:19 +0200 Subject: [PATCH 49/49] chore: relax clang-tidy rules for lesson 2.6 --- .devcontainer/.clang-tidy | 39 +++++++-------------------------------- CHANGELOG.md | 8 ++++++++ 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/.devcontainer/.clang-tidy b/.devcontainer/.clang-tidy index 0aa217a..492f6ce 100644 --- a/.devcontainer/.clang-tidy +++ b/.devcontainer/.clang-tidy @@ -9,44 +9,19 @@ Checks: > modernize-*, performance-*, readability-*, + -cppcoreguidelines-avoid-magic-numbers, + -modernize-use-trailing-return-type, -readability-avoid-const-params-in-decls, -readability-function-cognitive-complexity, + -readability-identifier-naming, + -readability-magic-numbers, misc-unused-using-decls, # Аналізувати лише код домашніх робіт і типові source/include директорії, # а не системні headers або сторонні бібліотеки. HeaderFilterRegex: '(^|/)(homework_[0-9]+|include|src)/' -# Помилки analyzer/core-guidelines/modernize мають ламати перевірку. -# Readability і performance лишаються попередженнями, щоб не блокувати -# навчальні ітерації надто рано. +# Зауваження static analyzer мають ламати перевірку: це найчастіше реальні баги. +# Решта checks лишаються warning-ами, щоб clang-tidy був навчальним +# інструментом, а не стіною стилістичних помилок для ДЗ. WarningsAsErrors: clang-analyzer-*, - cppcoreguidelines-*, - modernize-*, - -# Naming rules формують один стиль для прикладів курсу. -CheckOptions: - - key: readability-identifier-naming.FunctionCase - value: lower_case - - key: readability-identifier-naming.VariableCase - value: lower_case - - key: readability-identifier-naming.ConstantCase - value: CamelCase - - key: readability-identifier-naming.ConstantPrefix - value: k - - key: readability-identifier-naming.LocalVariableCase - value: lower_case - - key: readability-identifier-naming.ParameterCase - value: lower_case - - key: readability-identifier-naming.MemberCase - value: lower_case - - key: readability-identifier-naming.MemberSuffix - value: _ - - key: readability-identifier-naming.PrivateMemberCase - value: lower_case - - key: readability-identifier-naming.PrivateMemberSuffix - value: _ - - key: readability-identifier-naming.ClassCase - value: CamelCase - - key: readability-identifier-naming.StructCase - value: CamelCase diff --git a/CHANGELOG.md b/CHANGELOG.md index 7137ede..754633c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ Усі помітні зміни в цьому репо фіксуються тут. Формат - [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), дати в ISO 8601. +## 2026-05-08 + +### Changed + +- Block 2 / Lesson 2.6: послаблено `.clang-tidy` для ДЗ 6. Зауваження static + analyzer лишаються помилками, а style/modernize/core-guidelines сигнали + лишаються warning-ами для ручного розбору. + ## 2026-04-30 ### Added