diff options
author | Matthew Somerville <matthew-github@dracos.co.uk> | 2016-05-31 08:58:54 +0100 |
---|---|---|
committer | Matthew Somerville <matthew-github@dracos.co.uk> | 2016-05-31 15:36:08 +0100 |
commit | 38e2918987677253b6a337f5287c2e29685b9c78 (patch) | |
tree | 730f88f1cdef16d2e3c95cd6b95972277cb6b158 /bin | |
parent | a2b2bdd3813ad60c97269147efb8f0353becb66c (diff) |
Improve CSS compilation.
Move to using libsass via CSS::Sass, and stop using compass, supplying
any used mixins directly. This removes the need for any ruby/gem based
installation, and greatly increases the speed of compilation. make_css
is also enhanced, bringing in the file monitoring previously done by a
separate script and improving its dependency monitoring.
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/install-as-user | 33 | ||||
-rwxr-xr-x | bin/make_css | 168 | ||||
-rwxr-xr-x | bin/make_css_watch | 97 |
3 files changed, 144 insertions, 154 deletions
diff --git a/bin/install-as-user b/bin/install-as-user index 297efc1f3..99eca0b82 100755 --- a/bin/install-as-user +++ b/bin/install-as-user @@ -71,32 +71,6 @@ if [ ! "$DEVELOPMENT_INSTALL" = true ]; then echo $DONE_MSG fi -# Install the compass gem locally - it's required for generating the -# CSS. Don't trust the bundled bundler in e.g. precise. -echo "Setting up CSS... " -gem1.9.1 install --user-install --no-ri --no-rdoc bundler -FMS_GEMPATH="$DIRECTORY/gem-bin" -FMS_GEMPATH="$FMS_GEMPATH:$(ruby1.9.1 -rubygems -e 'puts Gem.user_dir')/bin" -export PATH="$FMS_GEMPATH:$PATH" - -if ! grep -q 'Set up local PATH for FixMyStreet' $HOME/.bashrc; then - cat >>$HOME/.bashrc <<EOBRC - -# Set up local PATH for FixMyStreet -export PATH="$FMS_GEMPATH:\$PATH" -EOBRC -fi - -bundle install --deployment --path "$DIRECTORY/gems" --binstubs "$DIRECTORY/gem-bin" - -# Use compass to generate the CSS, if it doesn't seem to already -# exist: -if [ ! -f web/cobrands/default/base.css ] -then - bin/make_css -fi -echo $DONE_MSG - # Write sensible values into the config file, if it doesn't already exist if [ ! -f conf/general.yml ]; then echo -n "Setting up default conf/general.yml file... " @@ -117,6 +91,13 @@ cd "$REPOSITORY" bin/install_perl_modules echo $DONE_MSG +echo "Setting up CSS... " +if [ ! -f web/cobrands/default/base.css ] +then + bin/make_css +fi +echo $DONE_MSG + # Create the database if it doesn't exist: echo -n "Setting up database... " if ! psql -l | egrep "^ *$DB_NAME *\|" > /dev/null diff --git a/bin/make_css b/bin/make_css index 87126ef28..29f2e8523 100755 --- a/bin/make_css +++ b/bin/make_css @@ -1,31 +1,137 @@ -#!/bin/bash -# -# make_css: -# Generate CSS files from SCSS files. -# -# Requires sass which you can get from http://sass-lang.com/ -# FixMyStreet cobrand requires compass -# -# Copyright (c) 2012 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org. WWW: http://www.mysociety.org - -COMPASS=compass -SASS=sass -DIR=$(cd "$(dirname "$0")" >/dev/null && pwd -P) -PARENT=$(cd "$DIR"/../.. >/dev/null && pwd) -if [ -f "$PARENT/gem-bin/compass" ]; then - COMPASS=$PARENT/gem-bin/compass - SASS=$PARENT/gem-bin/sass -fi - -DIRECTORY=$(cd "$DIR"/../web >/dev/null && pwd) - -DIRS=${@:-`find -L $DIRECTORY -name "*.scss" -exec dirname {} \; | uniq`} - -for dir in $DIRS; do - if [ -e "$dir/config.rb" ]; then - $COMPASS compile --output-style compressed $dir - else - $SASS --scss --update --style compressed $dir - fi -done +#!/usr/bin/env perl + +use strict; +use warnings; +use feature 'say'; + +BEGIN { + use File::Basename qw(dirname); + use File::Spec; + my $d = dirname(File::Spec->rel2abs($0)); + require "$d/../setenv.pl"; +} + +use CSS::Sass; +use File::ChangeNotify; +use File::Find::Rule; +use File::Slurp; +use Getopt::Long; +use Path::Tiny; +use Pod::Usage; + +# Store ARGV in case we need to restart later. +my @ARGVorig = @ARGV; + +GetOptions( + 'verbose' => \my $verbose, + 'watch' => \my $watch, + 'help|?' => \my $help, +) or pod2usage(2); +pod2usage(1) if $help; + +my $sass = CSS::Sass->new( + output_style => SASS_STYLE_COMPRESSED, + dont_die => 1, +); + +# Get directories from the command line, defaulting to 'web' if none. +# We need absolute paths so that the include files lookup works. +my @dirs = map { m{/} ? $_ : "web/cobrands/$_" } @ARGV; +@dirs = 'web' unless @dirs; +@dirs = map { path($_)->absolute->stringify } @dirs; + +# Initial compilation, to also discover all the included files. +my %includes; +my %include_to_main; +foreach my $file (File::Find::Rule->file->name(qr/^[^_].*\.scss$/)->in(@dirs)) { + my @includes = compile($file, $verbose); + $includes{$file} = \@includes; + map { push @{$include_to_main{$_}}, $file } @includes ? @includes : $file; +} + +# If we're not watching, we're done! +exit unless $watch; + +my $watcher = File::ChangeNotify->instantiate_watcher( + directories => [ @dirs, keys %include_to_main ], + filter => qr/\.scss$/, +); + +say "\033[34mWatching for changes\033[0m"; + +while ( my @events = $watcher->wait_for_events() ) { + for my $file (map { $_->path } @events) { + verbose($file, "%s was updated"); + for my $inc (@{$include_to_main{$file}}) { + my @includes = compile($inc, 1); + # From CSS::Sass::Watchdog test, we see includes are sorted + if (@includes && @{$includes{$inc}} && "@{$includes{$inc}}" ne "@includes") { + say "\033[34mRestarting to update includes\033[0m"; + exec( $^X, $0, @ARGVorig ) or die "Can't re-exec myself($^X,$0): $!\n"; + } + } + } +} + +# Given a SCSS file, compile it and generate a .map file, +# show an error if any, and return the list of includes. +sub compile { + my ($file, $verbose) = @_; + (my $output_file = $file) =~ s/scss$/css/; + my $source_map_file = "$output_file.map"; + + $sass->options->{source_map_file} = $source_map_file; + my ($css, $stats) = $sass->compile_file($file); + unless ($css) { + warn "\033[31m" . $sass->last_error . "\033[0m";; + return; + } + + my $written = write_if_different($output_file, $css); + if ($written) { + verbose($file, " \033[32mupdated\033[0m %s"); + } elsif ($verbose) { + verbose($file, " \033[33munchanged\033[0m %s"); + } + write_if_different($source_map_file, $stats->{source_map_string}); + return @{$stats->{included_files} || []}; +} + +# Write a file, only if it has changed. +sub write_if_different { + my ($fn, $data) = @_; + my $current = File::Slurp::read_file($fn, { binmode => ':utf8', err_mode => 'quiet' }); + if (!$current || $current ne $data) { + File::Slurp::write_file($fn, { binmode => ':utf8' }, $data); + return 1; + } + return 0; +} + +sub verbose { + my ($file, $format) = @_; + # Strip most of file path, keep dir/file + (my $pr = $file) =~ s{.*/(.*/.*)\.scss}{$1}; + say sprintf $format, $pr; +} + +__END__ + +=head1 NAME + +make_css - Generate CSS files from SCSS files, watch for changes. + +=head1 SYNOPSIS + +make_css [options] [dirs ...] + + Options: + --verbose display more information + --watch wait for file updates and compile automatically + --help this help message + +If no directories are specified, any .scss files under web/ that do not begin +with a "_" will be processed. "web/cobrands/" may be omitted from a given +directory. + +=cut diff --git a/bin/make_css_watch b/bin/make_css_watch deleted file mode 100755 index d15fa3a81..000000000 --- a/bin/make_css_watch +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env perl - -use strict; -use warnings; -use feature 'say'; - -BEGIN { - use File::Basename qw(dirname); - use File::Spec; - my $d = dirname(File::Spec->rel2abs($0)); - require "$d/../setenv.pl"; -} - -use Cwd qw(abs_path); -use File::ChangeNotify; -use File::Find::Rule; -use FindBin; -use Path::Tiny; - -my @exts = qw/ - scss -/; - -my @dirs = qw/ - web -/; - -my $filter = do { - my $exts = join '|', @exts; - qr/\.(?:$exts)$/ -}; - -my $watcher = File::ChangeNotify->instantiate_watcher( - directories => \@dirs, - filter => $filter, -); - -my $script_compass = 'compass'; -my $script_sass = 'sass'; -my $gem_bin = abs_path("$FindBin::Bin/../../gem-bin"); -if (-f "$gem_bin/compass") { - $script_compass = "$gem_bin/compass"; - $script_sass = "$gem_bin/sass"; -} - -sub title { - my $what = shift; - print "\033]2;$what\007\n"; -} - -say sprintf "Watching [%s] for %s", (join ',' => @dirs), $filter; -title 'watching'; - -while ( my @events = $watcher->wait_for_events() ) { - my %seen; - my @update_dirs; - title 'updating'; - for my $event (@events) { - my $file = path( $event->path ); - say "$file was updated..."; - my $dir = $file->dirname; - next if $seen{$dir}++; - - if ($dir eq 'web/cobrands/sass/') { - # contains only partials, so we don't need to update - # this directory, but we *do* need to update everything else - push @update_dirs, - grep { - ! ($seen{$_}++) - } - map { - path($_)->dirname - } - File::Find::Rule->file->name($filter)->in( @dirs ); - } - else { - push @update_dirs, $dir; - } - } - for my $dir (@update_dirs) { - if (-e "$dir/config.rb") { - system $script_compass, - 'compile', - '--output-style' => 'compressed', - $dir; - } - else { - system $script_sass, - '--scss', - '--update', - '--style' => 'compressed', - $dir; - } - title "$dir updated"; - } - title 'watching'; -} |