aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorMatthew Somerville <matthew-github@dracos.co.uk>2016-05-31 08:58:54 +0100
committerMatthew Somerville <matthew-github@dracos.co.uk>2016-05-31 15:36:08 +0100
commit38e2918987677253b6a337f5287c2e29685b9c78 (patch)
tree730f88f1cdef16d2e3c95cd6b95972277cb6b158 /bin
parenta2b2bdd3813ad60c97269147efb8f0353becb66c (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-xbin/install-as-user33
-rwxr-xr-xbin/make_css168
-rwxr-xr-xbin/make_css_watch97
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';
-}