Commit 99705af0 authored by ASN.1 Documenter's avatar ASN.1 Documenter
Browse files

Makefile to validate

parent 6a2e99f6
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
# This file is a template, and might need editing before it works on your project.
# Full project: https://gitlab.com/pages/doxygen
image: alpine

validate:
  stage: test
  script: make validate
  cache:
    paths:
      - asn2md.py
      - iso/*
  only:
    changes:
      - ./*.asn
    
documentation:
#  variables:
#    GIT_STRATEGY: clone
  stage: build
  script:
    - mkdir -p docs
    - python asn2md.py docs `find . -iname '*.asn'`
    - curl https://gitlab-ci-token:$CI_DOC_TOKEN@forge.etsi.org/rep/forge-tools/asn2md/raw/master/asn2md.py
    - make doc
    - git add docs/*.md
    - git commit -m "Documentation update"
    - git remote rm origin && git remote add origin https://gitlab-ci-token:$CI_DOC_TOKEN@forge.etsi.org/rep/$CI_PROJECT_PATH.git

Makefile

0 → 100755
+45 −0
Original line number Diff line number Diff line
ASN1_SRC   :=  IMZM-PDU-Descriptions.asn \
               cam/CAM-PDU-Descriptions.asn \
               vam/VAM-Temp-Imports.asn \
               cdd/ITS-Container.asn

ASN1_SRC_VALIDATE := \
               iso/iso19091_2018.asn \
               iso/iso24534-3_1_2015.asn \
               iso/iso24534-3_2_2015.asn \
               iso/ISO14816_AVIAEINumberingAndDataStructures.asn

ASN1_PDU := IMZM


ASN1CDIR    ?= $(USERPROFILE)/Work/asn1c-fillabs
ifneq (,$(ASN1CDIR))
  ASN1C := $(ASN1CDIR)/asn1c/.libs/asn1c -S $(ASN1CDIR)/skeletons
else
  ASN1C := asn1c
endif

validate: build iso build/gen_flag

doc: docs
	python asn2md.py docs $(ASN1_SRC)
		
build iso docs:
	mkdir -p $@

build/gen_flag: $(ASN1_SRC) $(ASN1_SRC_VALIDATE)
	$(ASN1C) -E -F -fcompound-names -fknown-extern-type=DATE -D build $(addprefix -pdu=,$(ASN1_PDU)) $^
clean:
	rm -rf build

iso/iso19091_2018.asn: 
	curl -o $@ 'https://standards.iso.org/iso/ts/19091/ed-2/en/ISO-TS-19091-addgrp-C-2018.asn'

iso/iso24534-3_1_2015.asn:
	curl -o $@ 'https://standards.iso.org/iso/24534/-3/ISO%2024534-3%20ASN.1%20repository/ISO24534-3_ElectronicRegistrationIdentificationVehicleDataModule_ForBallot.asn'

iso/iso24534-3_2_2015.asn:
	curl -o $@ 'https://standards.iso.org/iso/24534/-3/ISO%2024534-3%20ASN.1%20repository/ISO24534-3_ElectronicRegistrationIdentificationEfcVehicleDataModule_ForBallot.asn'

iso/ISO14816_AVIAEINumberingAndDataStructures.asn:
	curl -o $@ 'https://standards.iso.org/iso/14816/ISO14816%20ASN.1%20repository/ISO14816_AVIAEINumberingAndDataStructures.asn'

asn2md.py

deleted100755 → 0
+0 −162
Original line number Diff line number Diff line
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse # parse arguments
import os.path  # getting extension from file
import sys      # output and stuff
import re       # for regular expressions
import urllib   #

## extract doxygen-tag namespace
RE_MODULE   = re.compile( r'^\s*([A-Z][\w-]*)\s*({.*?})?\s*DEFINITIONS.*?::=\s*?BEGIN(.*)END', re.VERBOSE | re.MULTILINE | re.DOTALL)

RE_SPACES   = re.compile(r'\s+')

RE_COMMENTS = re.compile(r'^\s*--.*\n', re.MULTILINE)

RE_BASIC_TYPES = re.compile(r'^OCTET\s+STRING|BIT\s+STRING|BOOLEAN|INTEGER|FLOAT')

RE_FIELDS = re.compile(r'^\s*([\w-]+?)\s+(OCTET\s+STRING|BIT\s+STRING|[A-Z][.\w-]+)?(.*?)(?:,|$)', re.MULTILINE | re.DOTALL)

RE_EXPORTS = re.compile(r'^\s*EXPORTS.*?;', re.DOTALL | re.MULTILINE)

RE_IMPORTS = re.compile(r'^\s*IMPORTS\s*(.*?);', re.DOTALL | re.MULTILINE)

RE_IMPORT_ELEMENTS = re.compile(r'^([,\s\w-]*?)FROM\s*([\w-]+)\s*({[^}]*}(?:\s+WITH\s+SUCCESSORS)?)?', re.MULTILINE)

RE_IMPORT_ELEMENT_TYPE = re.compile(r'[^,\s]+')

RE_DOXY_COMMENTS = re.compile(r'^\s*--[-!#](:?$|\s(.*))', re.MULTILINE)

RE_DOXY_C_COMMENTS = re.compile(r'^\s*/\*\*\s(.*?)\*/', re.MULTILINE | re.DOTALL)

RE_DOXY_C_COMMENTS_I = re.compile(r'\s*\*')

RE_DOXY_REF = re.compile(r'@ref\s+([\w-]+)')
RE_DOXY_CLASS = re.compile(r'@class\s+([\w-]+)')
RE_DOXY_BRIEF = re.compile(r'@brief\s+')

RE_TYPE = re.compile(r'(([A-Z][\w-]*)\s*::=[\w \t]+(?:{(.*?)})?.*?)\n\s*\n', re.MULTILINE | re.DOTALL)

extTypes = {}

def parseText(content, indent=None):
	# we need to keep only documenting text under the /** */ and --!
	# convert --! comments to C-style ones
	c = RE_DOXY_COMMENTS.sub(r'/** \g<1>*/', content)
	if c is not None:
		content = c
	ret = ''
	for m in RE_DOXY_C_COMMENTS.finditer(content):
		lines = m.group(1).splitlines()
		for l in lines:
			l = RE_DOXY_C_COMMENTS_I.sub('', l, 1).rstrip()
			ret += ''.ljust(indent or 0) + l + '\n'
	def repl_ref(m):
		return '[**{0}**]({1}#{0})'.format(m.group(1), extTypes.get(m.group(1),''))
	c = RE_DOXY_REF.sub(repl_ref, ret)
	if c is not None:
		ret = c
	c = RE_DOXY_CLASS.sub('', ret)
	if c is not None:
		ret = c
	c = RE_DOXY_BRIEF.sub('', ret)
	if c is not None:
		ret = c
	def repl_category(m):
		ret = '\n&nbsp;&nbsp;&nbsp;&nbsp;Categories: '
		for l in m.group(1).split(','):
			ret += '[{0}](#{1}) '.format(l.strip(), urllib.quote_plus(l.strip()))
		return ret + '\n\n'
	c = re.sub(r'@category:\s+(.+)', repl_category, ret, re.MULTILINE)
	if c is not None:
		ret = c
	return ret
cpos=0
def parseModule(mname, content):
	global cpos
	cpos = 0
	ret = ''
	m = RE_IMPORTS.search(content)
	if m is not None:
		pos = 0
		if m.group(1) is not None:
			ret += '## Imports:\n'
			s = m.group(1)
			for fm in RE_IMPORT_ELEMENTS.finditer(s):
				imName = fm.group(2)
				for im in RE_IMPORT_ELEMENT_TYPE.finditer(fm.group(1)):
					extTypes[im.group(0)] = imName+'.md'
				ret += ' * **{}** *{}*<br/>\n'.format(imName, RE_SPACES.sub(' ', fm.group(3) or ''))
				ret += parseText(s[pos:fm.start()], 3)+'\n'
				pos = fm.end()
			ret += parseText(s[pos:])
		cpos = m.end()

	m = RE_EXPORTS.search(content)
	if m is not None:
		if cpos < m.end():
			cpos = m.end()
	
	# parse types
	def repl_type (m, doc):
		ret = '## <a name="{0}"></a>{0}\n\n'.format(m.group(2))
		if doc is not None:
			ret += parseText(doc) + '\n\n'
		# parse fields and get out fields descriptions
		if m.group(3) is not None:
			ret += 'Fields:\n'
			pos = 0
			for fm in RE_FIELDS.finditer(m.group(3)):
				if fm.group(2) is not None:
					f = fm.group(1).strip()
					t = fm.group(2).strip()
					if RE_BASIC_TYPES.match(t) is not None:
						ret += '* {0} **{1}** {2}<br>\n'.format(f, t, fm.group(3) or '')
					else:
						ret += '* {0} [**{1}**]({2}#{1}) {3}<br>\n'.format(f, t, extTypes.get(t,''), fm.group(3) or '')
				ret += parseText(fm.string[pos:fm.start()], 3)
				pos = fm.end()
		
		return ret + '```asn1\n' + RE_COMMENTS.sub('', m.group(1).strip()) +'\n```\n\n'

	pos = 0
	for m in RE_TYPE.finditer(content[cpos:]):
		ss = repl_type (m, m.string[pos:m.start()])
		ret += ss
		pos = m.end()
	return ret


def parseAsn(outDir, content) :
	# iterate modules in the file
	pos= 0
	cnt = 0
	for m in RE_MODULE.finditer(content):
		ret = '# ASN.1 module {}\n OID: _{}_\n'.format(m.group(1), RE_SPACES.sub(' ', m.group(2)))
		ret += parseText(content[pos:m.start()]) + '\n'
		if m.group(3) is not None:
			ret += parseModule(m.group(1), m.group(3))
		ret += '\n\n'
		open(outDir + '/' + m.group(1) + '.md', "w").write(ret)
		pos = m.end()
		cnt += 1
	return cnt

def main():
	argc = len(sys.argv)
	if argc < 3:
		sys.stderr.write("Usage: {} <out dir> [ASN.1 files]\n".format(sys.argv[0]))
		exit(1)
	outDir = sys.argv[1]
	cnt = 0
	for a in sys.argv[2:]:
		try:
			content = open(a).read()
			cnt += parseAsn(outDir, content)
		except IOError as e:
			sys.stderr.write(e[1]+"\n")
	print("{} modules porcessed\n".format(cnt))

if __name__ == '__main__':
	main()