Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .cursor/rules/snacclib7-release-workflow.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
description: esnacc version bumps, tags, and release workflow on feature branches
alwaysApply: true
---

# snacclib7 release workflow

## No release-notes markdown files

- **Do not** add or commit `release-notes-*.md` (or any markdown release-notes files).
- Document behavior in commit messages, Jira, or MR description instead.

## Version (`version.h`)

- **Once per feature branch:** when opening the branch, compare `version.h` on `main` and bump **one** patch (or agreed minor) — set `RELDATE` to the current date (`DD.MM.YYYY`).
- **Do not** bump again on the same branch while `main` is still behind that version.
- **Before opening the MR:** refresh `RELDATE` in `version.h` to the current date (no version number change unless `main` has shipped and a new release is intentional).

## Git tags

- **Do not** tag the feature branch for a release that is not yet on `main`.
- After **merge to `main`**, tag the release commit (e.g. `7.0/7.0.11` matching `version.h`).

## Global pin (`global` repo)

- **After** esnacc is merged to `main` (and tagged if applicable): update `libs/snacclib7` in `global` on a branch targeting `master`/`main`.
- Do **not** pin `global` to a pre-merge esnacc feature-branch tip for a version that is not on `main` yet.
- Rebuild `esnacc7.exe` into `global/buildtools/` from the merged `main` commit before or with the pin MR.
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ See [.cursor/rules/test-specification-first.mdc](.cursor/rules/test-specificatio
- When writing tests, focus on specifying the API — not on getting a green suite.
- **Only exception:** the user explicitly asks to change tests, change product code, or revise the spec.

## Version and tags (`version.h`)

See [.cursor/rules/snacclib7-release-workflow.mdc](.cursor/rules/snacclib7-release-workflow.mdc).

- Bump version **once** when opening a feature branch (compare to `main`); do not re-bump while `main` is still behind.
- Refresh `RELDATE` before opening the MR.
- Tag on **`main` after merge** (e.g. `7.0/7.0.11`); no release tags on the feature branch.
- Pin `global` **after** esnacc is on `main` (see release workflow rule).
- **No** `release-notes-*.md` files.

## Related repositories

- [esnacc-openapi-sdk](https://github.com/ESTOS/esnacc-openapi-sdk) — Swagger UI integration for generated OpenAPI output
Expand Down
32 changes: 32 additions & 0 deletions compiler/back-ends/structure-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,22 @@ bool IsDeprecatedFlaggedSequence(Module* mod, const char* szSequenceName)
return false;
}

bool IsIgnoreValidationExemptSequence(Module* mod, const char* szSequenceName, int validationCheck)
{
asnsequencecomment comment;
if (GetSequenceComment_UTF8(mod->moduleName, szSequenceName, &comment))
{
if (comment.iIgnoreValidation & validationCheck)
return true;
}
return false;
}

bool IsValidationExemptSequence(Module* mod, const char* szSequenceName, int validationCheck)
{
return IsDeprecatedFlaggedSequence(mod, szSequenceName) || IsIgnoreValidationExemptSequence(mod, szSequenceName, validationCheck);
}

bool IsDeprecatedNoOutputSequence(Module* mod, const char* szSequenceName)
{
if (!gi64NoDeprecatedSymbols)
Expand All @@ -601,6 +617,22 @@ bool IsDeprecatedFlaggedOperation(Module* mod, const char* szOperationName)
return false;
}

bool IsIgnoreValidationExemptOperation(Module* mod, const char* szOperationName, int validationCheck)
{
asnoperationcomment comment;
if (GetOperationComment_UTF8(mod->moduleName, szOperationName, &comment))
{
if (comment.iIgnoreValidation & validationCheck)
return true;
}
return false;
}

bool IsValidationExemptOperation(Module* mod, const char* szOperationName, int validationCheck)
{
return IsDeprecatedFlaggedOperation(mod, szOperationName) || IsIgnoreValidationExemptOperation(mod, szOperationName, validationCheck);
}

bool IsDeprecatedNoOutputOperation(Module* mod, const char* szOperationName)
{
if (!gi64NoDeprecatedSymbols)
Expand Down
8 changes: 8 additions & 0 deletions compiler/back-ends/structure-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ bool IsDeprecatedFlaggedMember(Module* mod, const TypeDef* td, const char* szEle
bool IsDeprecatedFlaggedSequence(Module* mod, const char* szSequenceName);
bool IsDeprecatedFlaggedOperation(Module* mod, const char* szOperationName);

// @ignorevalidation on SEQUENCE/OPERATION: skip selected ValidationLevel checks (bitmask).
bool IsIgnoreValidationExemptSequence(Module* mod, const char* szSequenceName, int validationCheck);
bool IsIgnoreValidationExemptOperation(Module* mod, const char* szOperationName, int validationCheck);

// True when a type or operation is exempt from a ValidationLevel check (@deprecated or matching @ignorevalidation bit).
bool IsValidationExemptSequence(Module* mod, const char* szSequenceName, int validationCheck);
bool IsValidationExemptOperation(Module* mod, const char* szOperationName, int validationCheck);

// Returns true when an element is flagged as deprecated AND shall not be written to the output
bool IsDeprecatedNoOutputModule(Module* mod);
bool IsDeprecatedNoOutputMember(Module* mod, const TypeDef* td, const char* szElement);
Expand Down
107 changes: 75 additions & 32 deletions compiler/core/asn_commentparser.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include "asn_commentparser.h"
#include "asn-stringconvert.h"
#include "filetype.h"
#include "snacc-validation-rules.h"
#include "../../snacc.h"
#include "time_helpers.h"
#include <stdio.h>
#include <sstream>
#include <string>
#include <list>
#include <string.h>
#include <string_view>
#include <time.h>
#include <vector>
#include <assert.h>
Expand All @@ -17,6 +19,21 @@

const std::string WHITESPACE = " \n\r\t\f\v";

namespace
{
constexpr std::string_view kTagBrief = "@brief";
constexpr std::string_view kTagLong = "@long";
constexpr std::string_view kTagPrivate = "@private";
constexpr std::string_view kTagDeprecated = "@deprecated";
constexpr std::string_view kTagAdded = "@added";
constexpr std::string_view kTagIgnoreValidation = "@ignorevalidation";
constexpr std::string_view kTagCategory = "@category";
constexpr std::string_view kTagLogfilter = "@logfilter";
constexpr std::string_view kTagLinked = "@linked";
constexpr std::string_view kTagClear = "@clear";
constexpr std::string_view kCommentIgnoredPrefix = "-- ~";
} // namespace

bool isFiltered(const ETypeComment& comment)
{
if (!gPrivateSymbols && comment.iPrivate)
Expand Down Expand Up @@ -72,6 +89,19 @@ std::string trim(const std::string& s)
return rtrim(ltrim(s));
}

namespace
{
[[nodiscard]] std::string remainderAfterTag(std::string_view line, std::string_view tag)
{
return std::string(line.substr(tag.size()));
}

[[nodiscard]] std::string trimmedRemainderAfterTag(std::string_view line, std::string_view tag)
{
return trim(remainderAfterTag(line, tag));
}
} // namespace

/**
* Converts a unix time into something readable
*
Expand Down Expand Up @@ -239,63 +269,76 @@ void convertCommentList(std::list<std::string>& commentList, ETypeComment* pType
int nEmptyLines = 0;
for (auto el = commentList.begin(); el != commentList.end(); el++)
{
std::string strLine = *el;
if (strLine.substr(0, 6) == "@brief")
std::string strLine = trim(*el);
if (strLine.starts_with(kTagBrief))
{
nEmptyLines = 0;
bInLong = false;
bInBrief = true;
strLine = trim(strLine.substr(6));
strLine = trimmedRemainderAfterTag(strLine, kTagBrief);
pType->strShort_UTF8 += escapeJsonString(strLine);
}
else if (strLine.substr(0, 5) == "@long")
else if (strLine.starts_with(kTagLong))
{
nEmptyLines = 0;
bInBrief = false;
bInLong = true;
strLine = trim(strLine.substr(5));
strLine = trimmedRemainderAfterTag(strLine, kTagLong);
pType->strLong_UTF8 += escapeJsonString(strLine);
}
else if (strLine.substr(0, 8) == "@private")
else if (strLine.starts_with(kTagPrivate))
{
nEmptyLines = 0;
pType->iPrivate = 1;
// We do not change the flags here, the keyword @private may lead or follow any comment
// bInLong = false;
// bInBrief = false;
}
else if (strLine.substr(0, 11) == "@deprecated")
else if (strLine.starts_with(kTagDeprecated))
{
nEmptyLines = 0;
pType->handleDeprecated(strLine.substr(11));
pType->handleDeprecated(remainderAfterTag(strLine, kTagDeprecated));
// We do not change the flags here, the keyword @deprecated may lead or follow any comment
// bInLong = false;
// bInBrief = false;
}
else if (strLine.substr(0, 6) == "@added")
else if (strLine.starts_with(kTagAdded))
{
nEmptyLines = 0;
pType->handleAdded(strLine.substr(6));
pType->handleAdded(remainderAfterTag(strLine, kTagAdded));
// We do not change the flags here, the keyword @deprecated may lead or follow any comment
// bInLong = false;
// bInBrief = false;
}
else if (strLine.substr(0, 9) == "@category")
else if (strLine.starts_with(kTagIgnoreValidation))
{
nEmptyLines = 0;
strLine = trim(strLine.substr(9));
std::string strRules = trimmedRemainderAfterTag(strLine, kTagIgnoreValidation);
std::string strError;
const unsigned int nMask = ParseIgnoreValidationRulesSpec(strRules, &strError);
if (!nMask)
{
fprintf(stderr, "*** %s: %s ***\n", strRules.empty() ? "@ignorevalidation" : "@ignorevalidation rule error", strError.c_str());
snacc_exit("Invalid @ignorevalidation tag.");
}
pType->m_nIgnoreValidationMask |= nMask;
}
else if (strLine.starts_with(kTagCategory))
{
nEmptyLines = 0;
strLine = trimmedRemainderAfterTag(strLine, kTagCategory);
// strLine += "\n";
pType->strCategory_UTF8 = escapeJsonString(strLine);
bInLong = false;
bInBrief = false;
}
else if (strLine.substr(0, 10) == "@logfilter")
else if (strLine.starts_with(kTagLogfilter))
{
nEmptyLines = 0;
EModuleComment* pModuleComment = static_cast<EModuleComment*>(pType);
if (pModuleComment)
{
strLine = trim(strLine.substr(10));
strLine = trimmedRemainderAfterTag(strLine, kTagLogfilter);
pModuleComment->strLogFilter = explode(strLine, ';');
}
}
Expand Down Expand Up @@ -360,39 +403,39 @@ void convertMemberCommentList(std::list<std::string>& commentList, EStructMember

for (auto el = commentList.begin(); el != commentList.end(); el++)
{
std::string strLine = *el;
if (strLine.substr(0, 6) == "@brief")
std::string strLine = trim(*el);
if (strLine.starts_with(kTagBrief))
{
last = eLast::_brief;
strLine = trim(strLine.substr(6));
strLine = trimmedRemainderAfterTag(strLine, kTagBrief);
pType->strShort_UTF8 += escapeJsonString(strLine);
}
else if (strLine.substr(0, 8) == "@private")
else if (strLine.starts_with(kTagPrivate))
{
last = eLast::_private;
pType->iPrivate = 1;
}
else if (strLine.substr(0, 11) == "@deprecated")
else if (strLine.starts_with(kTagDeprecated))
{
last = eLast::_deprecated;
pType->handleDeprecated(strLine.substr(11));
pType->handleDeprecated(remainderAfterTag(strLine, kTagDeprecated));
}
else if (strLine.substr(0, 6) == "@added")
else if (strLine.starts_with(kTagAdded))
{
last = eLast::_added;
pType->handleAdded(strLine.substr(6));
pType->handleAdded(remainderAfterTag(strLine, kTagAdded));
}
else if (strLine.substr(0, 7) == "@linked")
else if (strLine.starts_with(kTagLinked))
{
last = eLast::_linked;
strLine = trim(strLine.substr(7));
strLine = trimmedRemainderAfterTag(strLine, kTagLinked);
pType->strLinkedType_UTF8 += escapeJsonString(strLine);
}
else if (strLine.substr(0, 5) == "@long")
else if (strLine.starts_with(kTagLong))
{
// in case someone added a long comment to a member variable we add the content to the short
last = eLast::_brief;
strLine = trim(strLine.substr(5));
strLine = trimmedRemainderAfterTag(strLine, kTagLong);
if (!pType->strShort_UTF8.empty())
pType->strShort_UTF8 += escapeJsonString("\n");
pType->strShort_UTF8 += escapeJsonString(strLine);
Expand Down Expand Up @@ -432,7 +475,7 @@ int EAsnStackElementFile::ProcessLine(const char* szModuleName, const char* szRa
{
if (!szComment.empty())
{
if (szComment.substr(0, 6) == "@clear")
if (szComment.starts_with(kTagClear))
m_CollectComments.clear();
else
m_CollectComments.push_back(szComment);
Expand Down Expand Up @@ -504,7 +547,7 @@ int EAsnStackElementModule::ProcessLine(const char* szModuleName, const char* sz
{
if (!szComment.empty())
{
if (szComment.substr(0, 6) == "@clear")
if (szComment.starts_with(kTagClear))
m_CollectComments.clear();
else
m_CollectComments.push_back(szComment);
Expand Down Expand Up @@ -1136,7 +1179,7 @@ void EAsnCommentParser::FilterFiles()
auto strElements = explode(strFileContent, '\n', false, false);
for (auto& strElement : strElements)
{
if ((strElement.length() > 4 && strElement.substr(0, 5) == "-- ~ ") || (strElement.length() == 4 && strElement.substr(0, 4) == "-- ~"))
if (strElement.starts_with(kCommentIgnoredPrefix))
continue;

strElement += "\n";
Expand Down Expand Up @@ -1171,7 +1214,7 @@ int EAsnCommentParser::ProcessLine(const char* szModuleName, const char* szLine)
strLine = trim(strLine);

// Comments only have the first leading space removed
if (strComment.substr(0, 1) == " ")
if (strComment.starts_with(' '))
strComment = strComment.substr(1, strComment.size() - 1);

// strComment.TrimRight();
Expand All @@ -1180,7 +1223,7 @@ int EAsnCommentParser::ProcessLine(const char* szModuleName, const char* szLine)
strComment = " ";

// A comment starting with ~ is ignored
if (strComment.substr(0, 1) == "~")
if (strComment.starts_with('~'))
strComment.clear();
}
replaceAll(strLine, "\t", " ");
Expand Down Expand Up @@ -1251,4 +1294,4 @@ long long EModuleComment::GetModulePatchVersion()
}

return m_i64ModuleVersion;
}
}
4 changes: 4 additions & 0 deletions compiler/core/asn_commentparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class EStructMemberComment : public EDeprecated, public EAdded
class ETypeComment : public EDeprecated, public EAdded
{
public:
virtual ~ETypeComment() = default;

// Name of the strCategory
std::string strCategory_UTF8;
std::string strCategory_ASCII;
Expand All @@ -78,6 +80,8 @@ class ETypeComment : public EDeprecated, public EAdded
std::string strLong_ASCII;
// Type is private
int iPrivate = 0;
// Bitmask of ValidationLevel checks to skip (see snacc-validation-rules.h). 0 = not set.
unsigned int m_nIgnoreValidationMask = 0;
// Interal flag that stores whether the UTF8 value has already been converted to ascii (is done on access)
bool m_bConvertedToAscii = false;
};
Expand Down
Loading
Loading