Newer
Older
| Release of string * string
(* codename, version *)
| CurrentPlugin of string * string
(* plugin-name, plugin-version *)
| PluginRelease of string * string * string * string
(* plugin-name, plugin-version, kernel-name, kernel-version *)
(* tool-name, tool-version *)
type service =
| Cmd
| Main
| Gui
| Plugin of string * string option
| Service of string
type tag = Break | Bugfix | User | Developer | Commits
type text =
| Word of string
| Quoted of string
| PublicBts of int
| PrivateBts of int
| OldBts of int
| Item of char
| Space
| Newline
type entry = {
mutable service: service;
mutable tags: tag list;
mutable descr: text list;
}
let plugin_mode : string option ref = ref None
let current_plugin = ref None
let framac_services = [
"ACSL";
"Cil";
"Configure";
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"Doc";
"Journal";
"Kernel";
"Logic";
"Lib";
"Libc";
"Makefile";
"Project";
"Ptests";
"Typing"
]
let releases : (release * entry list) list ref = ref [ Current, [] ]
let file = ref "Changelog"
let line = ref 1
let error msg =
Format.eprintf "%s:%d: " !file !line;
Format.kfprintf
(fun fmt ->
Format.pp_print_newline fmt () ;
exit 2)
Format.err_formatter msg
let set_release cp r =
let entries = try List.assoc r !releases with Not_found -> [] in
let others = List.filter (fun (r',_) -> r<>r') !releases in
current_plugin := cp ;
releases := (r, entries) :: others
let add_entry e = function
| ( r , es ) :: olders -> ( r , e::es ) :: olders
| _ -> assert false
let set_name e a =
e.service <-
match !plugin_mode , !current_plugin , a with
| None , None , ("Cmd"|"CMD") -> Cmd
| None , None , ("Gui"|"GUI") -> Gui
| None , None , _ -> if List.mem a framac_services then Service a else Plugin(a,None)
| None , Some p , ("Cmd"|"CMD") -> Plugin(p,Some "Cmd")
| None , Some p , ("Gui"|"GUI") -> Plugin(p,Some "GUI")
| None , Some p , s -> Plugin(p,Some s)
| Some _ , None , s -> Service s
if (String.uppercase_ascii p)=(String.uppercase_ascii s) then Main else Service a
let set_tag e t = e.tags <- t :: e.tags
let mk_entry mark =
let entry = { service = Main; tags = []; descr = [] } in
String.iter
(function
| '-' -> set_tag entry User
| 'o' -> set_tag entry Developer
| '*' -> set_tag entry Bugfix
| '!' -> set_tag entry Break
| '+' -> set_tag entry Commits
| _ -> ())
mark ;
releases := add_entry entry !releases ;
entry
let add_word e w = match e.descr , w with
| Space :: _ , Space -> ()
| d , _ -> e.descr <- w :: d
}
let space = [' ' '\t']
let newline = ['\n' '\r']+
let upper = ['A'-'Z']
let ident = ['a'-'z' 'A'-'Z' '_' '0'-'9']
let file = ['a'-'z' 'A'-'Z' '_' '-' '0'-'9']
let digit = ['0'-'9']
let word = [^ ' ' '\t' '\n' '\r']
let mark = ['-' 'o' '*' '!' '+']
let vdigit = [ '0'-'9' '.' ]
(* ce is the current entry. None means no-entry at the beginning of line. *)
rule main ce = parse
| (mark+) as mark { entry (mk_entry mark) lexbuf }
| '#' [^ '\n']* '\n' { incr line; main None lexbuf }
| newline { incr line; main None lexbuf }
| eof { }
(* new Frama-C version name (major.minor (Element)) *)
| space* (("Open Source"|"Binary") space+)+ "Release" space+
((digit+) '.' (digit+) ('.' (digit+))? as version) space+
'(' ( (file+) as codename ) ')'
set_release None (Release(codename,version)) ;
main None lexbuf
}
| space* ("Plugin") space+ ((file+) as pname) space+
((digit+) '.' (digit+) ('.' (digit+))? as version) space+ '(' ( (file+) as codename ) ')'
{
set_release (Some pname) (Release(codename,version)) ;
main None lexbuf
}
| space* ("Plugin") space+ ((file+) as pname) space+ '<'[^'>']+'>'
{
set_release (Some pname) (Current) ;
main None lexbuf
}
| ( letter+ space+)* '<'[^'>']+'>'
{
set_release None (Current) ;
main None lexbuf
}
| space
{
match ce with
| None -> main ce lexbuf
| Some e -> text e lexbuf
}
| _
{
error "Misformed entry (%S)" (Lexing.lexeme lexbuf)
}
(* e is just created *)
and entry e = parse
| space { entry e lexbuf }
| (letter+ space+ letter+) as a { set_name e a ; text e lexbuf }
| (letter+) as a { set_name e a ; text e lexbuf }
| _ { error "Missing plugin or service name (at %S)" (Lexing.lexeme lexbuf) }
(* text of entry *)
and text e = parse
| '[' digit + '-' digit+ ('-' digit+)? ']' { (* date *) text e lexbuf }
| newline newline { incr line ; add_word e Newline ; main (Some e) lexbuf }
| newline { incr line ; main (Some e) lexbuf }
| space { add_word e Space ; text e lexbuf }
| eof { }
(* | space+ (digit as k) ')' { add_word e (Item k) ; text e lexbuf } *)
| "frama-c/" ((letter+) as sub) '#' ((digit+) as bug)
{ add_word e (Gitlab(false, sub, int_of_string bug)) ; text e lexbuf }
| '#' ((digit+) as bug)
{ add_word e (PublicBts(int_of_string bug)) ; text e lexbuf }
| "#!" ((digit+) as bug)
{ add_word e (PrivateBts(int_of_string bug)) ; text e lexbuf }
| "#?" ((digit+) as bug)
{ add_word e (OldBts(int_of_string bug)) ; text e lexbuf }
| "#@" ((digit+) as bug)
{ add_word e (Gitlab(false, "frama-c", int_of_string bug)) ; text e lexbuf }
| "##" ((digit+) as bug)
{ add_word e (Gitlab(true, "frama-c", int_of_string bug)) ; text e lexbuf }
| '-' (letter | '-')+
| '!'? (upper ident* '.')+ ident+
| '~' ident+
| '"' [^ '"' '\n']* '"'
| ( (file+ | '.' | "..") '/' ) * file+ '.' file+
| ( (file+ | '.' | "..") '/' ) + file+ '/'?
| '\\' ident+
{ add_word e (Quoted(Lexing.lexeme lexbuf)) ; text e lexbuf }
| letter+ ('-' letter+)*
{ add_word e (Word (Lexing.lexeme lexbuf)) ; text e lexbuf }
| _ { add_word e (Word (Lexing.lexeme lexbuf)) ; text e lexbuf }
{
let process filename =
let cin = open_in filename in
let lexbuf = Lexing.from_channel cin in
file := filename ;
line := 1 ;
main None lexbuf
let compare_string s1 s2 =
let cmp = String.compare (String.uppercase_ascii s1) (String.uppercase_ascii s2) in
if cmp = 0 then String.compare s1 s2 else cmp
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
let rec compare_strings s1 s2 =
match s1 , s2 with
| [] , _ -> (-1)
| _ , [] -> 1
| x::s1 , y::s2 ->
let cmp = compare_string x y in
if cmp = 0 then compare_strings s1 s2 else cmp
let service_compare s1 s2 =
let rank = function
| Main -> (0,[])
| Cmd -> (1,[])
| Gui -> (2,[])
| Service a -> (3,[a])
| Plugin(p,None) -> (4,[p])
| Plugin(p,Some a) -> (4,[p;a])
in
let (r1,a1) = rank s1 in
let (r2,a2) = rank s2 in
if r1=r2 then compare_strings a1 a2 else r1-r2
type version = {
major : int;
minor : int;
patch : int;
}
let extract_version s =
let re_version = Str.regexp "^\\([0-9]+\\)\\.\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?$" in
assert (Str.string_match re_version s 0);
let major = int_of_string (Str.matched_group 1 s) in
let minor = int_of_string (Str.matched_group 2 s) in
let patch =
try
int_of_string (Str.matched_group 4 s)
with _ -> 0
in
{ major; minor; patch }
(* compares both old-style (YYYYMMDD) and new-style numbering (MAJOR.MINOR)
note: codenames are only used for old versions or in case of equality
*)
let compare_version v1 v2 cn1 cn2 =
let re_old_numbering = Str.regexp ".*\\([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\\)" in
let is_old_numbering v = Str.string_match re_old_numbering v 0 in
let get_old_version v =
let v = String.map (fun c -> if c = '_' then '-' else c) v in
assert(Str.string_match re_old_numbering v 0) ;
Str.matched_group 1 v
in
match is_old_numbering cn1, is_old_numbering cn2 with
| true, true ->
let r = String.compare (get_old_version cn2) (get_old_version cn1) in
if r = 0 then String.compare v2 v1 else r
| false, true -> (* new numbering is always newer *) -1
| true, false -> (* old numbering is always older *) 1
| false, false -> (* compare new-style versions *)
let v1 = extract_version v1 in
let v2 = extract_version v2 in
let v = if v1.major = v2.major then if v1.minor = v2.minor then v2.patch - v1.patch
else v2.minor - v1.minor
else v2.major - v1.major
in
if v = 0 then (* same version number (1.0?); use lexicographic order *)
let compare_release (r1,_) (r2,_) =
match r1 , r2 with
| Current , Current -> 0
| Current , _ -> -1
| _ , Current -> 1
| CurrentPlugin (_,v1), CurrentPlugin (_,v2) -> String.compare v2 v1
| CurrentPlugin _, _ -> -1
| _ , CurrentPlugin _-> 1
| Release(cn1,v1) , Release(cn2,v2) -> compare_version v1 v2 cn1 cn2
| Release _ , _ -> -1
| _ , Release _ -> 1
| PluginRelease(_,v1,_,a1) , PluginRelease(_,v2,_,a2) ->
String.compare (a2 ^ v2) (a1 ^ v1)
| PluginRelease _ , _ -> -1
| _, PluginRelease _ -> 1
| ToolRelease(_,v1) , ToolRelease(_,v2) ->
String.compare v2 v1
let releases () =
List.map
(fun (r,es) -> r , List.filter (fun e -> not (List.mem Commits e.tags)) es)
(List.sort compare_release !releases)
}