rockbox/wps/wpsbuild.pl
Thomas Martitz 20e114c1a0 wpsbuild: The setting strings can now contain an additional feature constraint.
The setting strings are now of the form setting[.resolution[&feature]]
(resolution can be a regex, .+ to match all resultions). wpsbuild.pl will
check against features.txt to see if the target meets it.

This can be used, for example, to add/override default settings for
touchscreen devices.

Change-Id: I2eafa02f10b362a8e7de8e1f3a53115e70d28084
2014-03-03 15:14:15 +01:00

647 lines
17 KiB
Perl
Executable file

#!/usr/bin/perl
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id$
#
use strict;
use Getopt::Long qw(:config pass_through); # pass_through so not confused by -DTYPE_STUFF
use IPC::Open2;
my $ROOT="..";
my $wpsdir;
my $verbose;
my $rbdir=".rockbox";
my $tempdir=".rockbox";
my $wpslist;
my $target;
my $modelname;
# Get options
GetOptions ( 'r|root=s' => \$ROOT,
'm|modelname=s' => \$modelname,
'v|verbose' => \$verbose,
'rbdir=s' => \$rbdir, # If we want to put in a different directory
'tempdir=s' => \$tempdir, # override .rockbox temporary dir
);
($wpslist, $target) = @ARGV;
my $firmdir="$ROOT/firmware";
my $cppdef = $target;
# These parameters are filled in as we parse wpslist
my $req_t;
my $theme;
my $has_wps;
my $wps;
my $has_rwps;
my $rwps;
my $has_sbs;
my $sbs;
my $has_rsbs;
my $rsbs;
my $has_fms;
my $fms;
my $has_rfms;
my $rfms;
my $width;
my $height;
my $font;
my $remotefont;
my $fgcolor;
my $bgcolor;
my $statusbar;
my $remotestatusbar;
my $author;
my $backdrop;
my $lineselectstart;
my $lineselectend;
my $selecttype;
my $iconset;
my $remoteiconset;
my $viewericon;
my $showicons;
my $remoteviewericon;
my $lineselecttextcolor;
my $filetylecolor;
my $listviewport;
my $remotelistviewport;
# LCD sizes
my ($main_height, $main_width, $main_depth);
my ($remote_height, $remote_width, $remote_depth);
my $has_remote;
if(!$wpslist) {
print "Usage: wpsbuilds.pl <WPSLIST> <target>\n",
"Run this script in the root of the target build, and it will put all the\n",
"stuff in $tempdir/wps/\n and build the cfg according to $rbdir";
exit;
}
sub getlcdsizes
{
my ($remote) = @_;
my $str;
if($remote) {
# Get the remote LCD screen size
$str = <<STOP
\#include "config.h"
#ifdef HAVE_REMOTE_LCD
Height: LCD_REMOTE_HEIGHT
Width: LCD_REMOTE_WIDTH
Depth: LCD_REMOTE_DEPTH
#endif
STOP
;
}
else {
$str = <<STOP
\#include "config.h"
Height: LCD_HEIGHT
Width: LCD_WIDTH
Depth: LCD_DEPTH
STOP
;
}
close(GCC);
my $cmd = "gcc $cppdef -I. -I$firmdir/export -E -P -";
my $pid = open2(*COUT, *CIN, $cmd) or die "Could not spawn child: $!\n";
print CIN $str;
close(CIN);
waitpid($pid, 0);
my ($height, $width, $depth, $touch);
while(<COUT>) {
if($_ =~ /^Height: (\d*)/) {
$height = $1;
}
elsif($_ =~ /^Width: (\d*)/) {
$width = $1;
}
elsif($_ =~ /^Depth: (\d*)/) {
$depth = $1;
}
if($_ =~ /^Touchscreen/) {
$touch = 1;
}
}
close(COUT);
return ($height, $width, $depth);
}
sub mkdirs
{
mkdir "$tempdir/wps", 0777;
mkdir "$tempdir/themes", 0777;
mkdir "$tempdir/icons", 0777;
if( -d "$tempdir/wps/$theme") {
#print STDERR "wpsbuild warning: directory wps/$theme already exists!\n";
}
else
{
mkdir "$tempdir/wps/$theme", 0777;
}
}
sub normalize
{
my $in = $_[0];
# strip resolution
$in =~ s/(\.[0-9]*x[0-9]*x[0-9]*)//;
return $in;
}
sub copybackdrop
{
#copy the backdrop file into the build dir
if ($backdrop ne '') {
my $dst = normalize($backdrop);
system("cp $ROOT/$backdrop $tempdir/$dst");
}
}
sub copythemefont
{
#copy the font specified by the theme
my $o = $_[0];
$o =~ s/\.fnt/\.bdf/;
mkdir "$tempdir/fonts";
system("$ROOT/tools/convbdf -f -o \"$tempdir/fonts/$_[0]\" \"$ROOT/fonts/$o\" ");
}
sub copythemeicon
{
my $i = $_[0];
#copy the icon specified by the theme
if ($i ne "-") {
my $tempicon = $tempdir . "/" . $i;
$tempicon =~ /\/.*icons\/(.*)/i;
system("cp $ROOT/icons/$1 $tempicon");
}
}
sub uniq {
my %seen = ();
my @r = ();
foreach my $a (@_) {
unless ($seen{$a}) {
push @r, $a;
$seen{$a} = 1;
}
}
return @r;
}
sub copywps
{
# we assume that we copy the WPS files from the same dir the WPSLIST
# file is located in
my $dir;
my %skinfiles = ("wps", $wps,
"sbs", $sbs,
"fms", $fms,
"rwps", $rwps,
"rsbs", $rsbs,
"rfms", $rfms);
my @filelist;
my $file;
if($wpslist =~ /(.*)\/WPSLIST/) {
$dir = $1;
# copy fully-fledged wps, sbs, etc. including graphics
foreach my $ext (keys %skinfiles) {
next unless ($skinfiles{$ext});
$file = $skinfiles{$ext};
system("cp $dir/$file $tempdir/wps/$theme.$ext");
open(SKIN, "$dir/$file");
while (<SKIN>) {
$filelist[$#filelist + 1] = $1 if (/[\(,]([^,]*?.bmp)[\),]/);
}
close(SKIN);
}
if ($#filelist >= 0) {
if (-e "$dir/$theme") {
foreach $file (uniq(@filelist)) {
system("cp $dir/$theme/$file $tempdir/wps/$theme/");
}
}
else {
print STDERR "beep, no dir to copy WPS from!\n";
}
}
} else {
print STDERR "No source directory!\n";
}
}
sub buildcfg {
my $cfg = $theme . ".cfg";
my @out;
push @out, <<MOO
\#
\# $cfg generated by wpsbuild.pl
\# $wps is made by $author
\#
MOO
;
my %skinfiles = ("wps" => $wps,
"sbs" => $sbs,
"fms" => $fms,
"rwps" => $rwps,
"rsbs" => $rsbs,
"rfms" => $rfms);
for my $skin (keys %skinfiles) {
my $val = $skinfiles{$skin};
if (!defined($val)) {
# dont put value if not defined (e.g. rwps when there's no remote)
next;
} elsif ($val eq '') {
# empty resets to built-in
push @out, "$skin: -\n";
} else {
# file name is always <theme>.{wps,rwps,...} (see copywps())
push @out, "$skin: $rbdir/wps/$theme.$skin\n";
}
}
push @out, "selector type: $selecttype\n" if (defined($selecttype));
push @out, "backdrop: $backdrop\n" if (defined($backdrop));
push @out, "filetype colours: $filetylecolor\n" if (defined($filetylecolor));
if ($main_depth > 2) {
push @out, "foreground color: $fgcolor\n" if($fgcolor);
push @out, "background color: $bgcolor\n" if($bgcolor);
push @out, "line selector start color: $lineselectstart\n" if($lineselectstart);
push @out, "line selector end color: $lineselectend\n" if($lineselectend);;
push @out, "line selector text color: $lineselecttextcolor\n" if($lineselecttextcolor);
}
push @out, "font: $font\n" if (defined($font));
push @out, "statusbar: $statusbar\n" if (defined($statusbar));
push @out, "iconset: $iconset\n" if (defined($iconset));
push @out, "viewers iconset: $viewericon\n" if (defined($viewericon));
push @out, "show icons: $showicons\n" if (defined($viewericon) && defined($showicons));
push @out, "ui viewport: $listviewport\n" if (defined($listviewport));
if ($has_remote) {
push @out, "remote font: $remotefont\n" if (defined($remotefont));
push @out, "remote statusbar: $remotestatusbar\n" if (defined($remotestatusbar));
push @out, "remote iconset: $remoteiconset\n" if (defined($remoteiconset));
push @out, "remote viewers iconset: $remoteviewericon\n" if (defined($remoteviewericon));
push @out, "remote ui viewport: $remotelistviewport\n" if (defined($remotelistviewport));
}
if(-f "$tempdir/wps/$cfg") {
print STDERR "wpsbuild warning: wps/$cfg already exists!\n";
}
else {
open(CFG, ">$tempdir/themes/$cfg");
print CFG @out;
close(CFG);
}
}
# Get the LCD sizes first
($main_height, $main_width, $main_depth) = getlcdsizes();
($remote_height, $remote_width, $remote_depth) = getlcdsizes(1);
#print "LCD: ${main_width}x${main_height}x${main_depth}\n";
$has_remote = 1 if ($remote_height && $remote_width && $remote_depth);
# check if line matches the setting string or if it contains a regex
# that contains the targets resolution
#
# the line can be of the from
# 1) setting: value (handled above)
# 2) setting.128x96x2: value
# 3) setting.128x96x2&touchscreen: value
# The resolution pattern can be a perl-comatible regex
sub check_res_feature {
my ($line, $string, $remote) = @_;
if ($line =~ /^$string: *(.*)/i) {
return $1;
}
elsif($line =~ /^$string\.(.*): *(.*)/i) {
# $1 is a resolution/feature regex, $2 the filename incl. resolution
my $substr = $1;
my $fn = $2;
my $feature;
my $size_str = "${main_width}x${main_height}x${main_depth}";
if ($remote) {
$size_str = "${remote_width}x${remote_height}x${remote_depth}";
}
# extract feature constraint, if any
if ($substr =~ /&([a-z0-9_]+)/) {
$feature = $1;
$substr =~ s/&$feature//;
}
if ($size_str =~ /$substr$/) {
if ($feature) {
# check feature constrait
open(FEAT, "<apps/features");
chomp(my @lines = <FEAT>);
close(FEAT);
my $matches = (grep { /$feature/ } @lines);
return $fn if $matches > 0;
}
else {
# no feature constraint
return $fn;
}
}
}
return "";
}
# check if <theme>.<model>.<ext> exists. If not, check if <theme>.<ext> exists
sub check_skinfile {
my $ext = $_[0];
my $req_skin = $theme . "." . $modelname . ".$ext";
if (-e "$wpsdir/$req_skin") {
return $req_skin;
} else {
$req_skin = $theme . ".$ext";
if (-e "$wpsdir/$req_skin") {
return $req_skin;
}
}
return '';
}
# Infer WPS (etc.) filename from the the if it wasnt given
$wpslist =~ /(.*)WPSLIST/;
$wpsdir = $1;
open(WPS, "<$wpslist");
while(<WPS>) {
my $l = $_;
# remove CR
$l =~ s/\r//g;
if($l =~ /^ *\#/) {
# skip comment
next;
}
# prefix $rbdir with / if needed (needed for the theme.cfg)
unless ($rbdir =~ m/^\/.*/) {
$rbdir = "/" . $rbdir;
}
if($l =~ /^ *<theme>/i) {
# undef is a unary operator (!)
undef $theme;
undef $has_wps;
undef $has_rwps;
undef $has_sbs;
undef $has_rsbs;
undef $has_fms;
undef $has_rfms;
undef $wps;
undef $rwps;
undef $sbs;
undef $rsbs;
undef $fms;
undef $rfms;
undef $width;
undef $height;
undef $font;
undef $remotefont;
undef $fgcolor;
undef $bgcolor;
undef $statusbar;
undef $remotestatusbar;
undef $author;
undef $backdrop;
undef $lineselectstart;
undef $lineselectend;
undef $selecttype;
undef $iconset;
undef $remoteiconset;
undef $viewericon;
undef $showicons;
undef $remoteviewericon;
undef $lineselecttextcolor;
undef $filetylecolor;
undef $listviewport;
undef $remotelistviewport;
}
elsif($l =~ /^Name: *(.*)/i) {
$theme = $1;
}
elsif($l =~ /^Authors: *(.*)/i) {
$author = $1;
}
elsif ($l =~ /^WPS: *(yes|no)/i) {
$has_wps = $1;
}
elsif ($l =~ /^RWPS: *(yes|no)/i) {
$has_rwps = $1;
}
elsif ($l =~ /^SBS: *(yes|no)/i) {
$has_sbs = $1;
}
elsif ($l =~ /^RSBS: *(yes|no)/i) {
$has_rsbs = $1;
}
elsif ($l =~ /^FMS: *(yes|no)/i) {
$has_fms = $1;
}
elsif ($l =~ /^RFMS: *(yes|no)/i) {
$has_rfms = $1;
}
elsif($l =~ /^ *<main>/i) {
# parse main unit settings
while(<WPS>) {
my $l = $_;
if ($l =~ /^ *<\/main>/i) {
last;
}
elsif($_ = check_res_feature($l, "wps")) {
$wps = $_;
}
elsif($_ = check_res_feature($l, "sbs")) {
$sbs = $_;
}
elsif($_ = check_res_feature($l, "fms")) {
$fms = $_;
}
elsif($_ = check_res_feature($l, "Font")) {
$font = $_;
}
elsif($_ = check_res_feature($l, "Statusbar")) {
$statusbar = $_;
}
elsif($_ = check_res_feature($l, "Backdrop")) {
$backdrop = $_;
}
elsif($l =~ /^Foreground Color: *(.*)/i) {
$fgcolor = $1;
}
elsif($l =~ /^Background Color: *(.*)/i) {
$bgcolor = $1;
}
elsif($l =~ /^line selector start color: *(.*)/i) {
$lineselectstart = $1;
}
elsif($l =~ /^line selector end color: *(.*)/i) {
$lineselectend = $1;
}
elsif($_ = check_res_feature($l, "selector type")) {
$selecttype = $_;
}
elsif($_ = check_res_feature($l, "iconset")) {
$iconset = $_;
}
elsif($_ = check_res_feature($l, "viewers iconset")) {
$viewericon = $_;
}
elsif($l =~ /^show icons: *(.*)/i) {
$showicons = $1;
}
elsif($l =~ /^line selector text color: *(.*)/i) {
$lineselecttextcolor = $1;
}
elsif($l =~ /^filetype colours: *(.*)/i) {
$filetylecolor = $1;
}
elsif($_ = check_res_feature($l, "ui viewport")) {
$listviewport = $_;
}
}
}
elsif($l =~ /^ *<remote>/i) {
while(<WPS>) {
# parse remote settings
my $l = $_;
if ($l =~ /^ *<\/remote>/i) {
last;
}
elsif(!$has_remote) {
next; # dont parse <remote> section
}
elsif($_ = check_res_feature($l, "rwps", 1)) {
$rwps = $_;
}
elsif($_ = check_res_feature($l, "rsbs", 1)) {
$rsbs = $_;
}
elsif($_ = check_res_feature($l, "rfms", 1)) {
$rfms = $_;
}
elsif($_ = check_res_feature($l, "Font", 1)) {
$remotefont = $_;
}
elsif($_ = check_res_feature($l, "iconset", 1)) {
$remoteiconset = $_;
}
elsif($_ = check_res_feature($l, "viewers iconset", 1)) {
$remoteviewericon = $_;
}
elsif($_ = check_res_feature($l, "statusbar", 1)) {
$remotestatusbar = $_;
}
elsif($_ = check_res_feature($l, "ui viewport", 1)) {
$remotelistviewport = $_;
}
}
}
elsif($l =~ /^ *<\/theme>/i) {
# for each wps,sbs,fms (+ remote variants) check if <theme>[.<model>].wps
# exists if no filename was specified in WPSLIST
my $req_skin;
if ($has_wps eq "yes" && !$wps) {
$wps = check_skinfile("wps");
} elsif ($has_wps eq "no") {
$wps = '';
}
if ($has_sbs eq "yes" && !$sbs) {
$sbs = check_skinfile("sbs");
} elsif ($has_sbs eq "no") {
$sbs = '';
}
if ($has_fms eq "yes" && !$fms) {
$fms = check_skinfile("fms");
} elsif ($has_fms eq "no") {
$fms = '';
}
# now check for remote skin files (use main screen's extension)
if ($has_remote) {
if ($has_rwps eq "yes" && !$rwps) {
$rwps = check_skinfile("wps");
} elsif ($has_rwps eq "no") {
$rwps = '';
}
if ($has_rsbs eq "yes" && !$rsbs) {
$rsbs = check_skinfile("sbs");
} elsif ($has_rsbs eq "no") {
$rsbs = '';
}
if ($has_rfms eq "yes" && !$rfms) {
$rfms = check_skinfile("fms");
} elsif ($has_rfms eq "no") {
$rfms = '';
}
}
#print "LCD: $wps wants $width x $height\n";
#
# The target model has an LCD that is suitable for this
# WPS
#
#print "Size requirement is fine!\n";
mkdirs() if (-e "$wpsdir/$theme");
# Do the copying before building the .cfg - buildcfg()
# mangles some filenames
if (defined($backdrop) && $backdrop ne "-") {
copybackdrop();
$backdrop = "$rbdir/" . normalize($backdrop);
}
foreach my $i ($iconset, $viewericon, $remoteiconset, $remoteviewericon) {
if (defined($i) && $i ne "-") {
copythemeicon($i);
$i = "$rbdir/$i";
}
}
foreach my $i ($font, $remotefont) {
if (defined($i) && $i ne "-") {
copythemefont($i);
$i = "$rbdir/fonts/$i";
}
}
buildcfg();
copywps();
}
else{
#print "Unknown line: $l!\n";
}
}
close(WPS);