Simply invoke check_ycp
with the YCP file(s) you wish to check as
arguments:
check_ycp myfile.ycp
check_ycp *.ycp
The error messages should be self explanatory. They stick to the GNU standards for error messages, so you can use your favourite editor's (e.g. Emacs) function to process them.
Most of the checks can individually be turned off. Type
check_ycp -h
for a complete list of command line options. Those options are intentionally not listed here since such a list would inevitably be outdated before too long.
Even though using check_ycp
is pretty straightforward, some background
information is useful in order to understand what it does, its output and the
limitations of this tool - in short, what you can expect it to do and why not
to blindly rely on it.
check_ycp
is far from fool proof. In fact, it is pretty dumb. It just
tries to parse YCP code ("try" is the operative word here!) and applies various
checks for obvious programming errors. Some errors it will catch and report,
many it will not. But we (i.e. the YaST2 core development team) decided we'd
rather have a tool with limited capabilities than none at all.
Another reason for writing this document is pointing out why we try to enforce certain things, many of which are because of ergonomics or mere conventions in developing as a team, not true requirements of YaST2 or the YCP language.
The YaST2 team uses a standardized file header format for YCP modules. Standard fields are included there for various purposes - see the "why" sections of the individual checks.
Everything up to the first opening brace "{
" outside a comment is
considered part of the header. Nothing outside this portion of the file is
checked. You may, however, open and close as many comments as you like up to
this opening brace.
Leading asterisks "*
" at the start of lines are silently discarded
since they are often used to beautify multi line comments.
The comment markers themselves of course are also discarded for the checks:
/*
*/
//
Much code gets written by copying existing code. There is nothing wrong with
that (in fact, it saves a lot of work), but when you do, please change fields
accordingly - fields like Author
, Maintainer
,
Purpose
etc. - and the file name in Module
.
check_ycp
checks the file header for presence of at least one of
Author:
Authors:
Maintainer:
Maintainers:
If found, each entry is checked for some contents, i.e. it may not be completely empty (but use whitespace as you like).
The contents must include something that looks like an e-mail address.
There must be at least one person to contact when there are any problems or questions about the module. The full name is desired, but at least an e-mail address must be there to get in contact with the maintainer or the author.
Presence of a Id CVS / RCS identity marker is checked, e.g.
Id myfile.ycp,v 1.2 2001/02/14 18:04:50 sh Exp
This CVS / RCS ID is the only way of finding out exactly what CVS revision the
file has and what change date. The file date (what ls -l
shows) is
absolutely unreliable and irrelevant: This may have changed just by copying the
file around which didn't change anything.
This is important for bug tracking and for finding and fixing bugs - only when a developer knows what version of a file has been used he has a chance to reproduce a bug - or even make sure that a supposedly fixed bug didn't turn up again.
Presence of
Id:
is checked. There may be more characters before the closing dollar sign
"$
", but the exact contents is not checked.
![]() | Note |
---|---|
When creating a new file, it is absolutely sufficient to
include the unexpanded string (" |
If there is any message that is marked for translation with
_("
...")
, there must be a textdomain
statement.
The YaST2 translator module needs to know where to take the messages to be
translated from. This is what the textdomain
specification does.
Technically one textdomain
statement somewhere in the YCP program
would be sufficient, i.e. include files or modules called with
CallFunction()
don't really require an
additional textdomain
specification.
However, it is highy recommended all YCP files with translatable messages
include their own textdomain
statement so each YCP file is
self-sufficient in that regard, thus more easily reusable for other
purposes. This policy is enforced with this check.
After being stripped of all comments, the entire YCP code is scanned for the
translation marker sequence: An underscore immediately followed by an opening
parenthesis: _(
If this sequence is found, presence of translatable messages is assumed. If no
textdomain
statment is found there will be an error.
On the other hand, if there is no text to translate, a textdomain
statement is not necessary (but it can't hurt).
![]() | Note |
---|---|
Theoretically the " |
Literal strings in YCP code that contains HTML tags are usually help text that will be displayed in the YaST2 RichText widget. This HTML text is subjected to the sanity checks explained below.
Please notice that everything within double quotes "
will be
checked that contains anything surrounded by angle brackets
<
...>
- i.e. anything that looks remotely like an HTML
tag. Unknown tags will be silently ignored, but the entire text within the
quotes will be checked.
Limitation:
If a portion of help text lacks any HTML tag, it will not be checked since it
will not be recognized by check_ycp
as help text. Such completely
wrong portions of help text will slip through undetected, thus unchecked.
Each HTML text must start with a <p>
tag and end with a
</p>
tag.
There must be a corresponding closing </p>
tag for each opening
<p>
tag.
This is a basic requirement of HTML. The underlying YaST2 widgets may or may not be forgiving enough to tolerate missing tags, but we'd rather not rely on that.
Besides, no other types of paragraphs other than plain text paragraphs
<
p>
... <
/p>
are desired in
YaST2 help texts - in particular, no large font boldface headings etc.
See the intro of this section.
For each portion of HTML text:
<p>
tag is
permitted.</p>
tag is
permitted.</p>
and the next opening
<p>
tag is permitted.
See the intro of this section.
Each single portion of HTML text may contain exactly one paragraph, i.e. one
<
p>
... <
/p>
pair.
This is a convention to make life easier for the translators.
The tools used for extracting translatable texts from the sources (GNU
gettext
) detect differences between the last translated version of a
message and the current message from the latest source. They mark such messages
as fuzzy, i.e. the (human) translator is asked to have a good look at
it and decide whether there has been a real change in the message (thus it
needs to be retranslated) or just a cosmetic change (fixed typo, inserted
whitespace, reformatted the paragraph etc.).
This is a tedious task and it gets more tedious the longer each individual portion of text becomes. Changes from the old to the new version are hard to find if the portions are very long.
Plus, if they are that long it is very likely that always somewhere something has changed, thus the entire text is marked as fuzzy and needs careful manual checking which is not really necessary for all the text.
Split your help texts and use the YCP string addition operator to put them together.
Don't:
help_text = _("<p> bla blurb bla ... blurb bla blurb ... bla blurb bla ... </p> <p> bla blurb bla ... blurb bla blurb ... bla blurb bla ... </p>");
Instead, do:
// Help text (HTML like) help_text = _("<p> bla blurb bla ... blurb bla blurb ... bla blurb bla ... </p>"); // Help text (HTML like), continued help_text = help_text + _("<p> bla blurb bla ... blurb bla blurb ... bla blurb bla ... </p>");
Please also notice the comments for the translators just above the
text. The gettext
tools will automatically extract them along with the
text to translate and put them into the .po
file. The translators can
use them as additional hints what this text is all about.
See the intro of this section.
Such forced line breaks are plain superfluous. The HTML renderer will format the paragraph automatically - after each paragraph there will be a newline and some empty space to set each paragraph apart from the next.
There is no need nor is it desired to add extra empty space between paragraphs. This just looks plain ugly, even more so if this results in different spacings between several paragraphs of the same help text.
The most superfluous of those excess line breaks are those at the very end of a help text - after the last paragraph. Not only are they not good for anything, they sometimes even cause a vertical scroll bar to be displayed even though this would not be necessary otherwise.
Plus, there have been cases where erstwhile last help text paragraphs had been
rearranged so they now are in the middle of the help text - but unfortunately
the trailing <br>
tag had been forgotten and moved along with
the paragraph, thus causing different inter-paragraph spacings.
To make things even worse, fixing this breaks the translation for the affected paragraph: It will be marked as fuzzy just because of this even though it has not really changed.
We cannot entirely get rid of the <br>
tags (but we would like
to). Sometimes they are needed within paragraphs. But at least those
at the end of paragraphs we can do without.
Parameters to YaST2 UI widgets plus some commonly used functions
(e.g. Wizard::SetContents()
, Popup::Message()
etc.) are checked
where possible - if the parameters are simple string constants, maybe
surrounded by translation markers ("_("
...")
").
Optional widget parameters like `opt(...)
or `id(...)
are
ignored.
The following examples will be checked:
PushButton("OK");
PushButton( _("Cancel"));
PushButton(`id(`apply), _("Apply"));
PushButton(`opt(`default), _("OK"));
More complex parameters like variable contents or YCP terms cannot be checked.
The parser used in check_ycp
for that is really dumb. In fact, it only
scans for keywords like PushButton
outside string constants, tries to
find the corresponding matching pair of parentheses "(
...)
"
and splits everything inside into comma-separated subexpressions.
Only the most basic of those subexpressions are checked - only simple string
constants "
..."
or string constants marked for
translation _("
...")
.
The following examples will not be checked:
CheckBox( "/dev/"+device );
CheckBox( sformat("/dev/%1"), device );
CheckBox( GetDevName() );
string message = "OK"; PushButton( message );
Widgets that can have a keyboard shortcut (one character marked with an
ampersand "&
") are checked for presence of a keyboard shortcut.
![]() | Note |
---|---|
Consistency of the keyboard shortcuts is not checked,
only presence. |
This is for users whose mouse doesn't work (especially during installation time) as well as for experienced users who prefer working with the keyboard. Navigation from one widget to another is much easier when each widget that can get the keyboard focus can be reached with an [Alt] key sequence rather than repeatedly using the [Tab] key and/or the cursor keys.
There may be a lot more widgets that can have keyboard shortcuts than you expected. Basically, every widget that can somehow be operated with the keyboard (even if it is only scrolling) and that has an associated label (within, above or beside) can have a keyboard shortcut and should get one.
The widget parameter that acts as a label is checked for presence of exactly
one ampersand "&
".
See the widget checks of this section for more.
Widget parameters that are displayed literally as text are checked for
translation markers ("_("
...")
").
Every text message that ever gets to the end user is to be translated into the user's native language. This can only be made sure if the message is marked for translation.
See the intro of this section.
Presence of definitions of functions from the wizard lib (Package
yast2
) outside the wizard library itself is checked
such as Wizard::SetContents()
etc.
At the start of YaST2 develompent there was no other way of sharing code other
than simply copying it. Those days are gone; YCP now supports an
include
mechanism similar to C or C++.
Very general code like how to create the typical YaST2 wizard window layout has now been moved to the wizard lib, a collection of include files that provide such facilities. We want to get rid of duplicate code as soon as possible for obvious reasons (consistency, maintainability, efficiency).
Usage or presence of definitions of known obsolete functions is checked,
e.g. Popup::Message()
, Popup::YesOrNo()
etc.; using an equivalent
replacement function from the wizard lib's common_popups.ycp
include file is suggested.
Those functions are now superseded by those from
common_popups.ycp
. The replacement functions usually require less
parameters (thus are easier to use) and use a common and consistent widget
layout.
The definitions are checked very much like the wizard function definitions above; function and file names are hardwired here as well.
Usage of the obsolete functions is checked simply by checking for occurrence of the function name followed by an opening parenthesis (maybe with whitespace in between) somewhere in the code.
Presence of predefined message strings is checked, e.g. "&Next
",
"&Back
" etc.; using a corresponding function from the wizard
lib (Label
Module) is suggested,
e.g. Label::NextButton()
, Label::BackButton()
etc.
Ease the burden on the translators - with the predefined messages they don't need to translate the same standard texts over and over again.
Consistent messages for the same type of buttons etc. throughout all of YaST2.
Consistent keyboard shortcuts for the same button throughout all of YaST2.
If we ever need to change one of those standard messages, we can do that centralized.
The YCP code, stripped of comments, is checked for any one of the predefined
messages (including any keyboard shortcuts that may be there), surrounded by
translation markers ("_("
...")
").
Differences in spelling or only in whitespace will not be caught. If there is
no or another keyboard shortcut, the message will not be considered the same -
so if anybody uses "Ne&xt
" rather than "&Next
", this will go
undetected.
Alternative variable declarations are rejected, e.g.
string|void value = anything(); symbol|term result = UI::UserInput(); integer|string x = 42;
Just about the only situation where this made sense was when a variable might
sometimes be nil
to indicate some error condition. All other variants
of this are of purely academic nature or (more likely) poor programming
style. Since all YCP types can be nil
now, however, this feature
becomes totally redundant. It will very likely be dropped in the near future.
You probably don't want to perform all of the available checks for simple YCP
examples. Those should be concise and written for legibility rather than for
completeness. They will usually not contain a standard format file header with
all bells and whistles, no translation markers etc. - you don't want to bloat
HelloWorld.ycp
with all that stuff.
check_ycp
has a special example mode for just this purpose: It
turns off all checks that don't make sense for simple examples, yet allows you
to use check_ycp
anyway. If you think "well, what's left
then?" think about the future. check_ycp
can and will be expanded
to cover more and more checks, and even your examples can benefit from it.
For simple YCP examples (and only for them, please!) invoke check_ycp
with
the -x
command line option:
check_ycp -x HelloWorld.ycp
This turns off all checks that don't make sense for examples.
check_ycp
and Emacs go together well:
Invoke the Emacs compile command:
M-x compile
Edit the compile command ("make -k
" by default) in the
minibuffer; change it to the check_ycp
command you wish to invoke (you only need to do this once for each Emacs session):
check_ycp *.ycp
Use the next-error
function to go to the next error
check_ycp
has reported. The corresponding YCP file will automatically be
loaded into Emacs if needed, and Emacs will jump to the corresponding line
within that file.
If you haven't done so already, you might want to bind the compile
and
next-error
functions to keys in your ~/.emacs
file, e.g.
(global-set-key "f42" 'compile) (global-set-key "f43" 'next-error)
The real challenge here is to find a key that is not already in use for some other important function.
If you are a real hardcore YCP hacker, you can even go so far and change the
default compile command to check_ycp
in ~/.emacs
:
(setq compile-command "check_ycp *.ycp")
Everybody should be able to add checks for a new widget or a new function that uses keyboard shortcuts (unlikely) or translatable messages (very likely) - even without any knowledge of Perl:
Locate the check_widget_params()
function.
Add your widget to the list (the large regexp) near the function beginning, where all the other widgets are. Be careful not to include any whitespace (blanks or tabs) inside the parentheses. Wrong:
( MyNewWidget ) |
OK:
(MyNewWidget) |
Add an elsif()
branch to the large
if()
...elsif()
...elsif()
construction:
elsif ( $widget =~ /MyWidget/ ) { check_keyboard_shortcut ( $widget, $line_no, 1, @args ); check_translation ( $widget, $line_no, 1, @args ); }
You might have to change the third parameter according to your widget or
function: This is the number of the parameter to be checked (the first one is
1) after all `opt()
and `id()
parameters have been removed.
Of course you can omit the keyboard
shortcut check
(check_keyboard_shortcut()
) if it doesn't make sense for your
widget or function.
If there is more than one parameter to be checked for translatable messages, add a call to check_translation()
for
each.
Like Linus Torvalds once said: "Use the source, Luke!" ;-)
check_ycp
's sources are extensively commented, even the many regular
expressions used there. But changing those regexps really requires some
in-depth knowledge of regexps in general and Perl regexps in particular. If you
feel unsafe, better not touch them.
Other than that, use your creativity.